3
votes

In the following code:

class ExampleClass
  def initialize
    ObjectSpace.define_finalizer(self, proc{puts 'dead'})
  end
end

ex = ExampleClass.new
ex = nil

GC.start

while true
  # the while loop is to prevent the program from terminate
  # if the program does terminate, the finalizer gets called in the end like expected
end

The finalizer here is never called and there is no output. I would have expected the garbage collector to collect ex since it has been dereferenced. Why is GC.start not forcing ex to be collected and causing the finalizer to be called immediately?

1
I'm seeing dead print if I close your code using control+c as well as if I replace the while true end with exit. I'm on Ruby 2.5.0-dev fwiw. What platform are you on? - max pleaner
I'm on eclipse using Ruby 2.4.0 - aoiee
oh, and does the finalizer work without the GC.start? - max pleaner
I'm wondering why the dead does not print right when I call GC.start. I put a while loop there to show that the program does not terminate. - aoiee

1 Answers

2
votes

I believe that when you create a new Proc (proc just calls Proc.new, in Kernel)

Creates a new Proc object, bound to the current context.

Means that it's saving a reference to the self object and thus can never be de-referenced for the garbage collector to collect it. You should create the proc inside a class method instead so that the context becomes the class not the instance you're trying to destroy.

class ExampleClass
  def initialize
    ObjectSpace.define_finalizer(self, self.class.finalize)
  end

  def self.finalize
    proc { puts "dead" }
  end

end


ex = ExampleClass.new
# needed to do something with the object as well, not sure why,
# but it won't work without this line either
puts "object id = #{ex.object_id}"
ex = nil

GC.start
while true
end

and this outputs

object id = 70223915005060
dead
^Cexample.rb:20:in `<main>': Interrupt