On Tue, Nov 29, 2011 at 1:43 PM, Garthy D
<garthy_lmkltybr / entropicsoftware.com> wrote:
> I was wondering if running the (mark and sweep?) garbage collector manually
> is supposed to collect (and call define_finalizer procs on) objects with no
> remaining references. I would expect that the answer is yes, but it doesn't
> seem to actually work that way.

There are no guarantees whatsoever that *any* GC run will collect
particular unreachable instances.  The only guarantee is that all
finalizers are invoked eventually (unless of course in case of
catastrophic crash) - even if it is at process termination time.

> When I run the script below (based on some examples online that apparently
> avoid some of the problems with define_finalizer), the output suggests that
> the finalizer for an allocated and unused object of class A isn't actually
> being called until *after* the garbage collection is complete and the
> program has reached the end. This suggests that calling the garbage
> collector doesn't actually cause unreferenced objects to be reaped
> immediately.

Exactly.

> Is there a fault in my assumptions or the script below?

It's in your assumptions.  Note also that MRI won't bother to do any
GC run at all as long as memory stays below a certain threshold.

> If you wrap the A.new in a loop instead, eventually it'll free up the other
> instances of A en-masse (ie. calling the GC doesn't seem to do it, but
> flooding memory until the GC triggers does).
>
> I have seen similar behavior in a large embedded Ruby program that I am
> working on. In this case the Ruby objects in question have instance
> variables that reference textures, and I really need the finalizer to be
> called when all of the references to the textures are lost, so as to free up
> the texture memory. At the moment they are being finalised at program exit,
> when the available texture memory has long run out. This isn't good, and it
> means I need to rewrite every potential bit of this code to use manual
> reference counting.

No, that's a bad solution.  What you observe might mean that you
simply haven't created enough Ruby garbage for GC to think it needs to
work.  I have no idea how you allocate texture memory but if it is in
a C extension written by you I would check whether there is a way to
go through MRI's allocation in order to correct MRI's idea of used
memory.  Implementation of Ruby's String might be a good example for
that.

Another solution is to use transactions like File.open with a block
does.  Then you know exactly when the texture is not used any more and
can immediately release it (in "ensure").  Whether that is a viable
option depends on the design of your application.  See here:

http://blog.rubybestpractices.com/posts/rklemme/002_Writing_Block_Methods.html

> So basically: If the garbage collector is called, are objects with no
> remaining references supposed to be reaped during the call, and their
> defined finalizers called? Whatever the answer- why is that? Is there an
> official word on how this is supposed to work, and what can (and can't) be
> relied upon?

GC's prefer to decide themselves when and how they collect deadwood.
There are usually only very few guarantees (see JVM spec for an
example) in order to allow VM implementors maximum freedom and room
for optimization.  The only hard guarantee is that an object won't be
collected as long as it is strongly reachable.

Kind regards

robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/