Search code examples
rubygarbage-collectionfinalizer

Ruby forcing garbage collection not working as expected


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?


Solution

  • 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