The Trouble with Ruby Finalizers

I was test driving Devil, the developer’s image library, recently to see if it would work for us in a long-living daemon. Task #1 to that end is to verify the absence of memory leaks, which seem to be common in image libraries. It was almost immediately apparent that Devil contained a large memory leak. So I worked with John Mair to fix the issue.

Devil has a Devil::Image class which uses a finalizer to delete native resources when the image is garbage collected. The problem is that Ruby finalizers are notoriously difficult to use properly so often times they aren’t actually run. Here’s why:

class Devil::Image
    attr_reader :name, :file
 
    def initialize(name, file)
        @name = name
        @file = file
 
        ObjectSpace.define_finalizer( self, proc { IL.DeleteImages(1, [name]) } )
    end
end

So what’s wrong with this code? The issue is that the finalizer proc is a closure which holds a reference to it’s self, thus making it impossible for the image object to ever be garbage collected. When creating a finalizer proc, you should always use a class method to create the proc so that it does not hold a reference to the corresponding instance, like so:

  def initialize(name, file)
      @name = name
      @file = file
 
      ObjectSpace.define_finalizer( self, self.class.finalize(name) )
  end
  def self.finalize(name)
    proc { IL.DeleteImages(1, [name]) }
  end

A subtle and evil bug, just like its namesake!

11 thoughts on “The Trouble with Ruby Finalizers

  1. Evan Phoenix

    Agreed. The OS#define_finalizer API sadly invites this exact bug. Pretty much everyone who has even used OS#define_finalizer has hit this bug. Some catch it right away, but lots don’t.

    The API invites this bug because there the object isn’t passed to the block, and thusly people think they want the object to be in the closure to they can do something with it when it’s being finalized. Ruby’s GC can’t deal with this, and thus the passing of #object_id to the block.

    In Rubinius, I’m working on adding true finalizers: code that runs when an object was just detected as garbage, but can access the object itself. The finalizer code can even resurrect the object and it will be finalized later. Not yet sure what the API for this will be (a #finalize method on the object maybe?) but it should help in some ways.

  2. Pingback: Labnotes » Rounded Corners 249 — Life before Google

  3. Pingback: Delicious Bookmarks for March 25th from 14:22 to 16:56 « Lâmôlabs

  4. Jamie Cook

    Hey, i’m tying to figure out finalizers and came across this post but can’t get your example to work. There is a extraneous right curly brace in the line

    ObjectSpace.define_finalizer( self, self.class.finalize(name) } )

    and even after I remove that, the syntax doesn’t define a callable method. Instead it just calls the method :)

    You can wrap it in a Proc

    ObjectSpace.define_finalizer(self,proc{|id| self.class.finalize(name)})

    but that creates a closure which binds self, preventing the object from being garbage collected!

    How on earth are you meant to get access to the member variables from a finaliser?

  5. Mike Perham Post author

    Jamie, I’ve fixed the typo. You want it to call finalize; that method returns the actual proc that will be called to finalize the object.

    If you want access to the member variables, you have to pass them into the finalize() method so they are available to the finalizer, just like the name variable.

  6. Pingback: Ed Schmalzle » Blog Archive » TIL Ruby ObjectSpace#define_finalizer

  7. Pingback: Ruby IRC Logs 2011.11.24 | Techdot IRC Resources

  8. Pingback: D & Ruby FFI, part 1: mapping D structs onto Ruby classes « lomereiter's notes

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">