Here's an idea from this question: Upon object creation, increment a class variable. When object gets collected, decrement it. As you can observe, finalizer is called, and @@no_foo
gets decremented. But when I query it a moment later, decrement is gone. Seems that value is going only up, never down (if I create two objects, it will show 2). Am I missing something obvious?
class Foo
@@no_foo = 0
def initialize
puts 'creating object'
@@no_foo += 1
ObjectSpace.define_finalizer(self, proc { self.delete })
end
def delete
puts 'deleting object'
@@no_foo # => 1
@@no_foo -= 1
@@no_foo # => 0
end
def self.no_foo
@@no_foo # => 0, 1
end
end
Foo.no_foo # => 0
f = Foo.new
f = nil
GC.start
Foo.no_foo # => 1
# >> creating object
# >> deleting object
It can work, but there's circular reference in finalization. Your finalizer depends on the binding of an object that should be collected. See this solution.
class Foo
@@no_foo = 0
def initialize
@@no_foo += 1
ObjectSpace.define_finalizer(self, Foo.method(:delete))
end
def self.delete id # also this argument seems to be necessary
@@no_foo -= 1
end
def self.no_foo
@@no_foo
end
end
Foo.no_foo # => 0
1000.times{Foo.new}
Foo.no_foo # => 1000
GC.start
Foo.no_foo # => 0
Finalization is not happening when you think it should in the code you provided.
For example, if you change that one line to:
ObjectSpace.define_finalizer(self, proc do; puts "self is type #{self.class.name} and equals #{self.inspect}"; self.delete; end)
Then notice how it does nothing (even if I sit there and wait a while) until I kill irb:
... (entered class definition from above with that define_finalizer)
1.9.3-p392 :021 > Foo.no_foo # => 0
=> 0
1.9.3-p392 :022 > f = Foo.new
creating object
=> #<Foo:0x007fb5730f3e00>
1.9.3-p392 :023 > f = nil
=> nil
1.9.3-p392 :024 >
1.9.3-p392 :025 > GC.start
=> nil
1.9.3-p392 :026 > Foo.no_foo # => 1
=> 1
1.9.3-p392 :027 > ^D
self is type Foo and equals #<Foo:0x007fb5730f3e00>
deleting object
So the first assumption may be that GC was not invoked. But, lets look at it using GC::Profiler
:
1.9.3p392 :001 > GC::Profiler.enable
... (entered class definition from above)
1.9.3p392 :022 > puts GC::Profiler.result
GC 17 invokes.
Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)
=> nil
1.9.3p392 :023 > Foo.no_foo # => 0
=> 0
1.9.3p392 :024 > f = Foo.new
creating object
=> #<Foo:0x007fe2fc806808>
1.9.3p392 :025 > puts GC::Profiler.result
GC 17 invokes.
Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)
=> nil
1.9.3p392 :026 > f = nil
=> nil
1.9.3p392 :027 > puts GC::Profiler.result
GC 17 invokes.
Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)
=> nil
1.9.3p392 :028 > GC.start
=> nil
1.9.3p392 :029 > puts GC::Profiler.result
GC 18 invokes.
Index Invoke Time(sec) Use Size(byte) Total Size(byte) Total Object GC Time(ms)
1 0.161 997280 2257680 56442 3.96199999999999352696
=> nil
1.9.3p392 :030 > Foo.no_foo # => 1
=> 1
1.9.3p392 :031 > ^D
deleting object
So, it looks like the GC is getting invoked when you ask it to, but it is not finalizing the Foo instance until irb exit.