Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GC.start doesn't seem to actually collect the garbage

Tags:

ruby

I'm developing a class that has a finalizer method. In order to test the class, I want to run the garbage collector, then test some states to see if everything worked. However, I can't seem to actually get the garbage collected, so the finalization doesn't happen until the script ends. The following code shows the problem.

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

FinalCall.new
GC.start
puts 'done'

I had expected output like this:

finalizing
done

But I actually get this:

done
finalizing

What am I missing? How do you force GC to finalize everything that's out of scope?

like image 452
tscheingeld Avatar asked Sep 02 '25 09:09

tscheingeld


2 Answers

GC.start is not a guarantee that the garbage collector will run. It is only an advisory that it would be okay for your code if a GC happened at this particular point in time. Requiring the GC to run at a particular point in time would unreasonably constrain implementors. For example, it would be impossible to write a Ruby implementation for the JVM, the CLI, or the ECMAScript or PyPy platform, or to use frameworks like Eclipse OMR, since the GC is not under the control of the implementor.

There is no guarantee when a GC will happen. There is not even a guarantee that a GC will happen at all.

Therefore, there is also no guarantee about when or even if a finalizer will run.

like image 172
Jörg W Mittag Avatar answered Sep 05 '25 00:09

Jörg W Mittag


While Jörg is correct, you have an additional problem here: Your finalizer proc creates a closure over self which increments the reference count and then prevents your object from being collected!

The correct approach is to use a class method which creates a proc which does not close over instances. For example:

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

class FinalCall
  def initialize
    ObjectSpace.define_finalizer( self, self.class.finalizer )
  end

  def self.finalizer
    proc { puts 'finalizing FinalCall' }
  end
end

10.times do
  IncorrectFinalCall.new
  FinalCall.new
end
GC.start
puts 'done'

Resulting in:

ruby gc.rb
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
finalizing FinalCall
done
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
finalizing IncorrectFinalCall
like image 26
Chris Heald Avatar answered Sep 04 '25 23:09

Chris Heald



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!