finalizer not getting called

Discussion in 'Ruby' started by Joshua Chia, Aug 2, 2007.

  1. Joshua Chia

    Joshua Chia Guest

    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'
    --
    Posted via http://www.ruby-forum.com/.
     
    Joshua Chia, Aug 2, 2007
    #1
    1. Advertising

  2. 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
    --
    Posted via http://www.ruby-forum.com/.
     
    Wolfgang Nádasi-donner, Aug 2, 2007
    #2
    1. Advertising

  3. Joshua Chia

    Guest

    On Aug 1, 10:53 pm, "Wolfgang Nádasi-donner" <>
    wrote:
    > 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'

    >
    > ..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

    > Wolfgang Nádasi-Donner
    > --
    > Posted viahttp://www.ruby-forum.com/.
     
    , Aug 2, 2007
    #3
  4. 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.

    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
    --
    Posted via http://www.ruby-forum.com/.
     
    Wolfgang Nádasi-donner, Aug 2, 2007
    #4
  5. Joshua Chia

    Joshua Chia Guest

    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?
    --
    Posted via http://www.ruby-forum.com/.
     
    Joshua Chia, Aug 2, 2007
    #5
  6. Joshua Chia

    Alex Young Guest

    Joshua Chia wrote:
    > 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?

    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.

    --
    Alex
     
    Alex Young, Aug 2, 2007
    #6
  7. 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 >>>>>

    --
    Posted via http://www.ruby-forum.com/.
     
    Wolfgang Nádasi-donner, Aug 2, 2007
    #7
  8. 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'


    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

    --
    Florian Frank
     
    Florian Frank, Aug 3, 2007
    #8
  9. 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.

    --
    Florian Frank
     
    Florian Frank, Aug 3, 2007
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. chandu

    doubt abt finalizer method

    chandu, Dec 30, 2005, in forum: Java
    Replies:
    22
    Views:
    867
    Chris Smith
    Jan 4, 2006
  2. Apricot
    Replies:
    4
    Views:
    546
    velthuijsen
    Apr 16, 2004
  3. Weng Tianxiang
    Replies:
    6
    Views:
    597
    glen herrmannsfeldt
    Sep 12, 2007
  4. Helmut Jarausch

    module finalizer - is there such a beast?

    Helmut Jarausch, Jan 11, 2008, in forum: Python
    Replies:
    2
    Views:
    244
    Peter Otten
    Jan 11, 2008
  5. Francis Hwang
    Replies:
    1
    Views:
    104
    Yukihiro Matsumoto
    Nov 20, 2003
Loading...

Share This Page