On Fri, 25 Aug 2006, Zed Shaw wrote:

> Hi Folks,
>
> Sorry to get your attention.  :-)
>
> There's a very strange problem with Mongrel where if Threads are created
> because of the Mutex around Rails dispatching, then lots of ram gets
> created that never seems to go away.
>
> I boiled the problem down to this:
>
> http://pastie.caboo.se/10194
>
> It's a graph of the "leak" and the base code that causes it (nothing
> Mongrel in it at all).  This code kind of simulates how Mongrel is
> managing threads and locking Rails.
>
> What this code does is create threads until there's 1000 in a
> ThreadGroup waiting on a Mutex.  Inside the guard 30000 integers are put
> inside an Array.  Don't let this distract you since it can be strings,
> or even nothing and you'll see the same thing.  It's just to simulate
> Rails creating all the stuff it creates, and to demonstrate that while
> these objects should go away, they do not.
>
> Then it waits in 10 second increments for these threads to go away,
> calling GC.start each time.
>
> And what happens is the graph you see (samples of mem usage of the ruby
> process 1/second after 3 cycles of create/destroy threads).  Rather than
> the memory for the threads and the array of integers going away, it
> sticks around.  It'll dip a little bit, but not much, just tops out
> there and doesn't die.  Even though all the threads are clearly gone and
> none of their contents should be around.
>
> In contrast, if you remove the Mutex then the ram behaves as you'd
> expect, with it going up and then going away.
>
> I'm hoping people way smarter with Ruby than myself can tell me why this
> happens, what is wrong with this code, and how to fix it.
>
> Thanks.

hi zed-

i don't think you have a leak.  try running under electric fence (ef).  when i
do i clearly see the memory rise from 1->20% on my desktop, and then decline
back down to 1%, over and over with no reported leaks.  the cycle matches
the logging of the script perfectly.

here's the thing though, when i don't run it under electric fence i see the
memory climb to about 20% and then stay there forever.  but this too does not
indicate a leak.  it just shows how calling 'free' in a process doesn't really
release memory to the os, only to the process itself.  the reason you see the
memory vary nicely under ef is that it replaces the standard malloc/free with
it's own voodoo - details of which i do not understand or care too.  the
point, however, is that it's 'free' which is doing the 'leaking' - just at the
os level, not the process (ruby) level.  we have tons of really long running
processes that exhibit the exact same behaviour - basically the memory image
will climb to maximum and stay there.  oddly, however, when you tally them all
up the usage exceeds the system capacity plus swap by miles.

i think this is just illustraing reason 42 why i prefer Kernel.fork to
Thread.new  - the only real way to return memory to the os is to exit! ;-)

regards.

-a
-- 
to foster inner awareness, introspection, and reasoning is more efficient than
meditation and prayer.
- h.h. the 14th dalai lama