Mike Perham

On Ruby, software and the Internet

The Trouble with Ruby Finalizers

February 24th, 2010 · 3 Comments

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!

Tags: Ruby

3 responses so far ↓

  • 1 Evan Phoenix // Mar 1, 2010 at 1:02 am

    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 Labnotes » Rounded Corners 249 — Life before Google // Mar 1, 2010 at 10:37 am

    [...] How to forget. On the common mistake of finalizing with a closure reference. [...]

  • 3 Delicious Bookmarks for March 25th from 14:22 to 16:56 « Lâmôlabs // Mar 25, 2010 at 4:16 pm

    [...] The Trouble with Ruby Finalizers – March 25th %(postalicious-tags)( tags: ruby bugs programming finalizer gc leak memory garbage collection )% [...]

Leave a Comment