On Wed, Sep 17, 2008 at 10:07 AM, Brian Candler <b.candler / pobox.com> wrote:
> I wondered if someone can explain the following behaviour to me.
>
> For some reason, in the following code the instances of object Foo are
> prevented from being garbage collected.
>
>  def make_thread
>    Thread.new { sleep 2 }
>  end
>
>  class Foo
>    attr_accessor :bar
>  end
>
>  10.times { |i|
>    f = Foo.new
>    f.bar = make_thread
>  }
>
>  GC.start
>  sleep 1
>  GC.start
>
>  ObjectSpace.each_object(Foo) { |o| p o }
>
> But if I modify the code as follows, then the Foo objects are
> garbage-collected just fine:
>
> --- tst.rb      2008-09-17 10:00:27.000000000 +0100
> +++ tst2.rb     2008-09-17 10:03:04.000000000 +0100
> @@ -6,9 +6,10 @@
>     attr_accessor :bar
>   end
>
> +  threads = (0..9).collect { make_thread }
>   10.times { |i|
>     f = Foo.new
> -    f.bar = make_thread
> +    f.bar = threads[i]
>   }
>
>   GC.start
>
> It's as if the thread created by Thread.new keeps a reference to the Foo
> object which existed at the time, even though inside make_thread it is
> out of scope, so it shouldn't even know about it.
>
> By tweaking the 'sleep' values, it also seems that when the thread
> terminates it then does permit the Foo instance to be garbage-collected.
>
> Any ideas as to what's going on? I get the same results on these two
> versions of Ruby:
>
> ruby 1.8.4 (2005-12-24) [i486-linux]
> ruby 1.8.6 (2008-03-03 patchlevel 114) [i686-linux]
>
> Thanks,
>
> Brian.
> --

The threads are not garbage collected until they terminate and so the
Foo instances are not being GC'd. You're not sleeping long enough at
the end of your script. Try using something like:

 GC.start
 sleep 20
 GC.start

and see how many instances are still hanging around at the end of your script.

Regards,
Sean