Subject:
Re: [ruby-ffi] Proper memory management
From:
Wayne Meissner
Date:
5/5/10 10:13 PM
To:
ruby-ffi@googlegroups.com

Well,
<hint>
There is a wiki over at http://wiki.github.com/ffi/ffi/ that could use
an entry on FFI::AutoPointer, so next time someone needs the thing,
they could find the info there.  You could put in your experiences
with using it.

</hint>

On 6 May 2010 12:25, v01d <phreakuencies@gmail.com> wrote:
> Ah, that's much better. I didn't know I could use it that way.
>
> Thanks!
>
> On 05/05/2010 11:19 PM, Wayne Meissner wrote:
>>
>> Use FFI::AutoPointer - its there for exactly this reason (having one
>> implementation of the whole "clean up native memory when ruby object
>> instance is finalized" is better than having many, slightly broken
>> variants).
>>
>> FFI::AutoPointer also has the advantage on JRuby of _not_ using
>> ObjectSpace, so a) it will actually work in jruby-1.5.0 and later
>> where objectspace is not on by default, and b) is less overhead, since
>> its implemented via standard java PhantomReferences.
>>
>> Use it something like this:
>>
>> class VectorPointer<  FFI::AutoPointer
>>
>>   def self.release(ptr)
>>    # ptr is the original pointer you passed in to create VectorPointer
>>     GSLng.backend.gsl_free_vector(ptr)
>>   end
>> end
>>
>> Then use it like thus:
>>  ptr = VectorPointer.new(GSLng.backend.gsl_alloc_vector(n))
>>
>> It is a FFI::Pointer subclass, so you can pass it wherever a :pointer
>> parameter is needed, or use it in a Struct :pointer field, etc.
>>
>> There is also AutoPointer#free() to free the memory earlier than the
>> GC cleanup (which is a good idea when you can use it), #autorelease=()
>> to toggle on/off the auto cleanup.
>>
>>
>> On 6 May 2010 08:56, 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!
>>>
>