Subject:
Re: [ruby-ffi] Proper memory management
From:
Jeremy Voorhis
Date:
5/5/10 8:02 PM
To:
ruby-ffi@googlegroups.com

I know from discussions over irc that Evan Phoenix isn't too keen on using finalizers for memory management for this very reason. In my own projects, I've found myself occasionally writing #dispose methods for manual object deletion, along with a block form of the constructor that ensures #dispose is called after the block is run. If controlling when memory should be freed is important, it's probably best not to rely on MRI's GC!

Best

Jeremy

On Wed, May 5, 2010 at 3:56 PM, v01d <phreakuencies@gmail.com> wrote:
Hi,
I'm in the process of developing a Ruby binding to the GSL numerical
library using FFI. Recently I stumbled upon a problematic issue
regarding memory management and finalizers.

Since the GSL library allows creation and destruction of instances
like this:
gsl_vector* ptr = gsl_alloc_vector(size_t n);
...
gsl_free_vector(ptr);

Then, in Ruby I do something like this:

class Vector
 def initialize(n)
   @ptr = GSLng.backend.gsl_alloc_vector(n)
   Vector.define_finalizer(self, @ptr)
 end

 def Vector.define_finalizer(self, ptr)
   ObjectSpace.define_finalizer(self, lambda {|id|
GSLng.backend.gsl_free_vector(ptr)})
 end
end

The problem is that if later I instatiante a Vector inside a loop,
like:
10000.times do
 Vector.new(3)
end

This obviously eats a lot of memory until GC runs. Now, if I do:
10000.times do
 Vector.new(3)
 GC.start
end

the memory that the Vector instances themselves occupy is freed but
the finalizer is not called (it is later called at program end),
therefore all of the memory allocated through gsl_alloc_vector() is
sitting there until program ends.

I know that Ruby doesn't guarantee that finalizers are called in any
particular moment (even after GC.start, which I thought would
suffice). So what I'm asking probably is not really related to FFI
itself, but I wanted to ask here since I imagined this would be a
common pattern among FFI users. The question is then: is there a
better way to manage this type of memory? I obviously cant use the
transaction-like pattern (like File.open) since for a Vector that
wouldn't make sense. It would be ideal to make the gsl_vector_free
call when the actual Vector class is free'd. Maybe I need to go to a
lower level for this and use the C api to register this call in the
"free" function for this class, but that would be overkill since I
wanted to avoid using C altogether.

Thank you!