Garbage collection and define_finalizer

Hi.

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.

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.

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

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.

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?

Any thoughts?

Cheers,
Garthy


#!/usr/bin/ruby -w
###!/packages/ruby/bin/ruby -w

class A

def initialize
ObjectSpace.define_finalizer(self,
self.class.method(:finalize).to_proc)
#ObjectSpace.define_finalizer(self, self.class.finalize2)
@a = 1
$stderr.print “Init A\n”
end

def self.finalize(id)
$stderr.print “Finalise called for: #{id}.\n”
end

def self.finalize2
proc {$stderr.print “Finalise called.\n”}
end

end

def do_gc
GC.start
#ObjectSpace.garbage_collect
end

def foo
a = A.new
nil
end

foo

$stderr.print “Starting garbage collection.\n”
do_gc
$stderr.print “Finished garbage collection.\n”

On Nov 29, 2011, at 6:43 AM, Garthy D wrote:

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.

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?

The finalizer behavior is undefined at the language level because it is
runtime-dependent. Try running your code on JRuby or Rubinius. Each has
a different GC than MRI and will thus behave differently. The GC in MRI
is also different between 1.8 and 1.9 when it was replaced with a more
efficient mechanism.

You can’t rely on the GC calling your finalizers when the object is
reaped. Each runtime will have different behavior.

I have a project that uses a finalizer to deallocate some native memory
(allocated via FFI). I see the same behavior as you describe on MRI. On
JRuby and Rubinius the finalizer is called “closer in time” to when the
object is reaped but I don’t know for certain that it is happening in
the same cycle as the GC. For all I know it’s pushing the finalizer
reference onto another queue somewhere and getting to it during its idle
time.

So, try another runtime or modify your code to do refcounting.

cr

On Tue, Nov 29, 2011 at 1:43 PM, Garthy D
[email protected] 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.

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

Hi Chuck,

Thankyou for the fast reply. :slight_smile:

On 29/11/11 23:42, Chuck R. wrote:

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.

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?

The finalizer behavior is undefined at the language level because it is
runtime-dependent. Try running your code on JRuby or Rubinius. Each has a
different GC than MRI and will thus behave differently. The GC in MRI is also
different between 1.8 and 1.9 when it was replaced with a more efficient
mechanism.

Thankyou for the information. Leaving the GC/finalizer behaviour
undefined is reasonable- it just causes me problems in my particular
circumstances. :confused:

I haven’t experimented with other Ruby implementations (just the MRI),
but it is good to know that other implementations may also handle it
differently. Basically: I can assume very little about how the GC might
behave.

You can’t rely on the GC calling your finalizers when the object is reaped. Each
runtime will have different behavior.

Yes. Unfortunate for me, but understandable.

I have a project that uses a finalizer to deallocate some native memory
(allocated via FFI). I see the same behavior as you describe on MRI. On JRuby and
Rubinius the finalizer is called “closer in time” to when the object is reaped but
I don’t know for certain that it is happening in the same cycle as the GC. For all
I know it’s pushing the finalizer reference onto another queue somewhere and
getting to it during its idle time.

It’s nice to know that I’m not going crazy. :wink: Because a great deal of
information online suggests that the memory is cleaned up immediately
(on GC), I had assumed this was the case, and that I must have had a bug
or unknown reference to certain objects somewhere in my code. As you can
probably imagine, that took some time to track down.

So, try another runtime or modify your code to do refcounting.

I was hoping to avoid needing to layer reference counting on top of
things, but I might not have a great deal of choice in the matter. I’ll
just have to think of ways to make it work well in my particular
circumstances. Darn.

Garth

On Wed, Nov 30, 2011 at 4:11 AM, Garthy D
[email protected] wrote:

Thankyou for the detailed reply. :slight_smile:

You’re welcome!

On 30/11/11 00:07, Robert K. wrote:

On Tue, Nov 29, 2011 at 1:43 PM, Garthy D
[email protected] wrote:

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.

Your assumption re the C extension for texture memory is pretty-much spot
on. :slight_smile:

:slight_smile:

Re texture memory, it’s a little more complicated than a standard
allocation. Some memory will be the standard allocated sort, but some will
be on the video card, and they’re effectively coming from different “pools”
(or heaps), neither of which I’ll have direct control over. Unless the Ruby
GC directly understands this concept (I don’t know if it does, but I’m
guessing not), I’m not going to be able to use Ruby to manage that memory.
Unfortunately, the problem goes a bit beyond just textures, as I’m wrapping
a good chunk of a 3D engine in Ruby objects. That’s my problem to worry
about though.

Sounds tricky indeed.

And that’s assuming I can redirect the allocation calls anyway- I’m not sure
if I can. It’d be nice to be able to inform the Ruby GC of allocated memory
(or an estimate, if it keeps changing) without actually leaving the GC to
allocate it. Please correct me if I’m wrong, but I’m assuming this can’t be
done, and you must use the ALLOC/ALLOC_N-style functions?

I’m not such a good source for this as the source (or Matz). So you
probably better look elsewhere for a definitive answer. Maybe for
your issue it is sufficient to just reduce the threshold from which on
Ruby starts considering GC at all. I can’t really tell.

implementation should immediately clean up these references on next GC
(which, as we’ve established, isn’t accurate)?

I am not aware of any. I haven’t searched extensively though. But we
might need to update locations you found to reflect the truth.
Ideally someone from core team would do a writeup which explains how
GC works - at least for MRI. Chances are also that behavior has
changed between 1.8 and 1.9 as these are quite different internally.

Kind regards

robert

Hi Robert,

Thankyou for the detailed reply. :slight_smile:

On 30/11/11 00:07, Robert K. wrote:

catastrophic crash) - even if it is at process termination time.
I suspected this might be the case- and it’s certainly not an
unreasonable assumption to make of a GC.

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.

Your assumption re the C extension for texture memory is pretty-much
spot on. :slight_smile:

Re texture memory, it’s a little more complicated than a standard
allocation. Some memory will be the standard allocated sort, but some
will be on the video card, and they’re effectively coming from different
“pools” (or heaps), neither of which I’ll have direct control over.
Unless the Ruby GC directly understands this concept (I don’t know if it
does, but I’m guessing not), I’m not going to be able to use Ruby to
manage that memory. Unfortunately, the problem goes a bit beyond just
textures, as I’m wrapping a good chunk of a 3D engine in Ruby objects.
That’s my problem to worry about though.

And that’s assuming I can redirect the allocation calls anyway- I’m not
sure if I can. It’d be nice to be able to inform the Ruby GC of
allocated memory (or an estimate, if it keeps changing) without actually
leaving the GC to allocate it. Please correct me if I’m wrong, but I’m
assuming this can’t be done, and you must use the ALLOC/ALLOC_N-style
functions?

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

Thankyou for the link. It does not appear to be suitable for say
textures in my specific case (they are long-lived and freed at a later
time), but may help with some of the other problems I need to solve.

collected as long as it is strongly reachable.
This is completely reasonable of course.

Unfortunately it causes a lot of problems in my case, as I had assumed
one thing from general reading on the topic, and observed another.
That’s my problem to deal with though, not anyone else’s. Still, there
seems to be lot of information online that talks about Ruby performing
mark and sweep, either stating outright that unreferenced objects are
freed on first GC, or heavily implying it at least. From your
description, and my observations, this information appears to be
incorrect. Basically, there seems to be a lot of misinformation about
what the GC is doing. Apart from the source, is there some place where
the correct behaviour is discussed, that could be referred to instead,
particularly if someone is suggesting that the MRI GC implementation
should immediately clean up these references on next GC (which, as we’ve
established, isn’t accurate)?

Garth

Hi Robert,

On 30/11/11 23:51, Robert K. wrote:

[email protected] wrote:
on. :slight_smile:
a good chunk of a 3D engine in Ruby objects. That’s my problem to worry
about though.

Sounds tricky indeed.

It certainly keeps things interesting. :wink:

And that’s assuming I can redirect the allocation calls anyway- I’m not sure
if I can. It’d be nice to be able to inform the Ruby GC of allocated memory
(or an estimate, if it keeps changing) without actually leaving the GC to
allocate it. Please correct me if I’m wrong, but I’m assuming this can’t be
done, and you must use the ALLOC/ALLOC_N-style functions?

I’m not such a good source for this as the source (or Matz). So you
probably better look elsewhere for a definitive answer.

I had a dig around the source (gc.c in particular) to see if I could
find any answers, but I’m afraid it’s beyond me at this point. I’d
hazard a guess that the functionality doesn’t yet exist, but I could
very well be completely wrong. It’s just a guess at this stage.

Maybe for
your issue it is sufficient to just reduce the threshold from which on
Ruby starts considering GC at all. I can’t really tell.

On thinking about it, I really need to solve the problem “properly”,
particularly for large memory-guzzling things such as textures and 3D
models. As an experiment I set up a means to force an object to drop its
data ahead of time (where “drop” is defined as deleting the user data,
but keeping the Ruby object), effectively invalidating it. For textures
(and similar entities), the plan is to give each entity that requires
one its own copy of a wrapped smart pointer. Thus the textures will be
freed once every entity holding a texture object has either manually
dropped its copy, or the GC has collected any references I’ve missed
(and the plan of course is to try not to miss any references!). I’ve
got it working on simple objects at this point. I’ll have to recode a
few things to get it going for textures and models, but this is
probably the solution I’ll go for. I’ll have to see how it turns out
in practice.

However, once this is done for the bigger entities, there will still be
quite a large number of small entities with minimal additional memory
footprint that Ruby is unaware of. Reducing the threshold may be enough
to solve the problem for the majority of these small entities- thankyou
for the suggestion. :slight_smile:

implementation should immediately clean up these references on next GC
(which, as we’ve established, isn’t accurate)?

I am not aware of any. I haven’t searched extensively though. But we
might need to update locations you found to reflect the truth.
Ideally someone from core team would do a writeup which explains how
GC works - at least for MRI. Chances are also that behavior has
changed between 1.8 and 1.9 as these are quite different internally.

I’ll keep an eye out for such a thing if it appears.

Garth

On Tue, Nov 29, 2011 at 7:12 AM, Chuck R. [email protected]
wrote:

I have a project that uses a finalizer to deallocate some native memory
(allocated via FFI). I see the same behavior as you describe on MRI. On JRuby and
Rubinius the finalizer is called “closer in time” to when the object is reaped but
I don’t know for certain that it is happening in the same cycle as the GC. For all
I know it’s pushing the finalizer reference onto another queue somewhere and
getting to it during its idle time.

That is, in fact, what Hotspot does. If you get a thread dump from a
running OpenJDK JVM, you’ll see something like this:

“Finalizer” daemon prio=5 tid=0x000000010086f800 nid=0x106c80000 in
Object.wait() [0x0000000106c7f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)

  • waiting on <0x00000007f5965e70> (a
    java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
  • locked <0x00000007f5965e70> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)

It waits for finalizable object references to be pushed onto its queue
and then runs their finalizers.

  • Charlie

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs