Forum: Ruby finalizer not getting called

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
688b35f9becc72d8ca0d2fbc76749119?d=identicon&s=25 Joshua Chia (syncopated)
on 2007-08-02 06:09
The finalizer is not getting called in this code -- it just prints
'exiting'.  What's wrong?  I'm running it on v1.8.6 on Windows Vista.

class A
  def initialize
     ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
  end
end
a = A.new
a = nil
GC.start
sleep(5)
puts 'exiting'
A131b672fdbd2a58dce12031ad78b121?d=identicon&s=25 Wolfgang Nádasi-Donner (wonado)
on 2007-08-02 07:53
Joshua Chia wrote:
> The finalizer is not getting called in this code -- it just prints
> 'exiting'.  What's wrong?  I'm running it on v1.8.6 on Windows Vista.
>
> class A
>   def initialize
>      ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
> #{id}"})
>   end
> end
> a = A.new
> a = nil
> GC.start
> sleep(5)
> puts 'exiting'

I used this example with Windows2000 and the same result. It works when
I put the finalizer definition outside of the class definition.

>>>>> Code >>>>>
class A
  def initialize
    puts "initializing #{self.object_id}"
#    ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
  end
end
a = A.new
puts "created #{a.object_id}"
ObjectSpace.define_finalizer(a, proc{|id| puts "finalizing #{id}"})
a = nil
GC.start
puts 'exiting'
>>>>> Output >>>>>
initializing 24036900
created 24036900
exiting
finalizing 24036900
>>>>> EOE >>>>>

When I change your original code a little bit, there will be an
interesting result...

>>>>> Code >>>>>
class A
  def initialize
    puts "initializing #{self.object_id}"
    ObjectSpace.define_finalizer(self, proc{|id| puts "finalizing
#{id}"})
  end
end
a = A.new
puts "created #{a.object_id}"
#ObjectSpace.define_finalizer(a, proc{|id| puts "finalizing #{id}"})
a = nil
GC.start
ObjectSpace.each_object(A){|o|puts "still there: #{o.object_id}"}
puts 'exiting'
>>>>> Output >>>>>
initializing 24036690
created 24036690
still there: 24036690
exiting
>>>>> EOE >>>>>

...which means, that the object was not destroyed by the GC. I don't see
any additional actual reference to the object and don't understand this.

Wolfgang Nádasi-Donner
49d779534da06c3718084bce68f53e66?d=identicon&s=25 evanwebb@gmail.com (Guest)
on 2007-08-02 10:22
(Received via mailing list)
On Aug 1, 10:53 pm, "Wolfgang Nádasi-donner" <ed.oda...@wonado.de>
wrote:
> > a = A.new
> > a = nil
> > GC.start
> > sleep(5)
> > puts 'exiting'
>
> ..which means, that the object was not destroyed by the GC. I don't see
> any additional actual reference to the object and don't understand this.
>

There is actually a simple reason. Remember that you're passing a
block, ie. a closure, to define_finalizer. That closure holds on to
the stuff like self, which in this case is the object to be finalized.
Thus the finalizer code is actually keeping the object alive.

Use define_finalizer outside the object associates the block with a
context that does not contain the object to be finalized (once the
local is set to nil), so it's actually garbage, and the finalizer
runs.

This is the most common misstep when using finalizers, and they should
be avoided if at all possible, because remember, there is no guarantee
when a finalizer will run, or if it will even run at all.

 - Evan Phoenix // evan () fallingsnow [] net
A131b672fdbd2a58dce12031ad78b121?d=identicon&s=25 Wolfgang Nádasi-Donner (wonado)
on 2007-08-02 12:06
evanwebb@gmail.com wrote:
> There is actually a simple reason. Remember that you're passing a
> block, ie. a closure, to define_finalizer. That closure holds on to
> the stuff like self, which in this case is the object to be finalized.
> Thus the finalizer code is actually keeping the object alive.

Oh yes, this has to be overlook easily. Usually I don't care about the
time objects will be destroyed.

evanwebb@gmail.com wrote:
> ...there is no guarantee ... if it will even run at all.

I understand from the description, that a finalizer will run for an
object directly after GC destroyes it. It will not be called if the
program ends without calling GC ever, but if GC destroyes it, the call
will be guaranteed. Is this correct?

Wolfgang Nádasi-Donner
688b35f9becc72d8ca0d2fbc76749119?d=identicon&s=25 Joshua Chia (syncopated)
on 2007-08-02 19:30
evanwebb@gmail.com wrote:
> ...
> There is actually a simple reason. Remember that you're passing a
> block, ie. a closure, to define_finalizer. That closure holds on to
> the stuff like self, which in this case is the object to be finalized.
> Thus the finalizer code is actually keeping the object alive.
>
> Use define_finalizer outside the object associates the block with a
> context that does not contain the object to be finalized (once the
> local is set to nil), so it's actually garbage, and the finalizer
> runs.

I'm not familiar with all the nuances for blocks, but I only see that
the block makes no reference to self.  Why does it need to hold on to a
reference to self?

With traditional C code, when you use a library, the client app often
needs to manually call init and teardown.  With C++ and Java, you can
let the library do the init and teardown with constructors and
destructors, so you don't need to depend on the app to call init and
teardown at the right time.  This is good  from a software engineering
perspective since it reduces the number of things entangling the client
and library.

I'm primarily concerned about the teardown happening at all and
secondarily about it happening as soon as possible.  How do I guarantee
the eventual execution of some teardown code without depending on the
client (in this case, whoever initialized the instance)?  Do I just move
the call to define_finalizer into another method and call that method
from initialize?
Ad7805c9fcc1f13efc6ed11251a6c4d2?d=identicon&s=25 Alex Young (regularfry)
on 2007-08-02 19:47
(Received via mailing list)
Joshua Chia wrote:
>> runs.
> perspective since it reduces the number of things entangling the client
> and library.
>
> I'm primarily concerned about the teardown happening at all and
> secondarily about it happening as soon as possible.  How do I guarantee
> the eventual execution of some teardown code without depending on the
> client (in this case, whoever initialized the instance)?  Do I just move
> the call to define_finalizer into another method and call that method
> from initialize?
The Ruby way is not to rely on a finalizer, but to define your setup and
teardown code around a yield thus:

class Foo
   def do_work
     setup_bits
     yield
     teardown_bits
   end
end

Then you use it like this:

Foo.new.do_work do
   whatever_you_want
end

That way the execution sequence is
  - setup_bits
  - whatever_you_want
  - teardown_bits

This is far simpler than relying on finalizer behaviour, which I find
rather impenetrable.
A131b672fdbd2a58dce12031ad78b121?d=identicon&s=25 Wolfgang Nádasi-Donner (wonado)
on 2007-08-02 19:50
Joshua Chia wrote:
> I'm not familiar with all the nuances for blocks, but I only see that
> the block makes no reference to self.  Why does it need to hold on to a
> reference to self?

Blocks act as closures, which means, that variables not defined as block
parameter but used inside the block, will be taken from the environment
in which the block was defined. In this case the value of "self" - which
refers the new defined object - must be saved for this purpose.

If you define the block outside of initialize, it will work.

>>>>> Code >>>>>
class A
  def initialize(b)
    puts "initializing #{self.object_id}"
    ObjectSpace.define_finalizer(self, b)
  end
end
a = A.new(proc{|id| puts "finalizing #{id}"})
puts "created #{a.object_id}"
a = nil
GC.start
ObjectSpace.each_object(A){|o|puts "still there: #{o.object_id}"}
puts 'exiting'
>>>>> Result >>>>>
initializing 24036640
created 24036640
finalizing 24036640
exiting
>>>>> EOE >>>>>
D84df7c68f790e492c4ad4ec5fe65547?d=identicon&s=25 Florian Frank (Guest)
on 2007-08-03 02:28
(Received via mailing list)
Joshua Chia wrote:
> a = nil
> GC.start
> sleep(5)
> puts 'exiting'

If the answer is finalizer in Ruby, then it's very likely, that you're
working on the wrong problem. They are quite difficult to get right
(=not referencing the value to be finalized anywhere). You could use a
class variable for example:

class A
   @@foo = proc{|id| puts "finalizing #{id}"}

   def initialize
     ObjectSpace.define_finalizer(self, @@foo)
   end
end

Another possibility is to use a binding, that doesn't
D84df7c68f790e492c4ad4ec5fe65547?d=identicon&s=25 Florian Frank (Guest)
on 2007-08-03 02:42
(Received via mailing list)
Florian Frank wrote:
> Another possibility is to use a binding, that doesn't

Ahmmm. Let's continue here: ...contain the value, perhaps
TOPLEVEL_BINDING and evaluate a string in this context:

class A
   def initialize
      ObjectSpace.define_finalizer(self,
       eval('proc{|id| puts "finalizing #{id}"}', TOPLEVEL_BINDING))
   end
end

def foo
   a = A.new
end
foo # don't taint the toplevel
GC.start
sleep(5)
puts 'exiting'

Like I said, it's not pretty to use finalizers in Ruby and you perhaps
shouldn't. Using the yield pattern mentioned by Alex Young is perhaps a
better solution to your problem.
This topic is locked and can not be replied to.