Hi,
We’ve been using the JRuby-Rack JMS support in production for while
now, since JRuby 1.1.6. It’s a Rails app running in Glassfish which
additionally receives messages from ActiveMQ. It works well, but we’ve
always had a memory leak issue with it. I’d been hoping that the
problem would be fixed by e.g. JRUBY-3217, but it’s still a problem on
1.4.0, with JRuby-Rack 0.9.5 or the 0.9.6 snapshot. We’ve managed by
restarting every few weeks, but I’ve now got a minimal test case which
I believe reproduces the problem.
The heap dump from a Glassfish instance which has been running a while
has a large number of RubyThread, Frame and ThreadContext objects.
Looking at the Glassfish logs, it’s clear that the app server isn’t
just using one ActiveMQ Session thread, but is periodically starting a
new one, so over time we’re calling into a the same JRuby runtime from
a large number of different Java threads.
The test case is here:
What I’m trying to do there is make a number of trivial calls into
JRuby from each of 25000 threads, just dumping out the current thread
name. After all the threads have run to completion (verified in
jconsole), looking at the jmap histogram shows a number of classes
with counts very close to 25000 or multiples thereof:
chris-mbp-en1:~ chris$ jmap -histo:live 17183 |head -20
num #instances #bytes class name
1: 250021 20001680 org.jruby.runtime.Frame
2: 25143 4865832 [Ljava.util.WeakHashMap$Entry;
3: 29514 4319000
4: 25001 4200168 org.jruby.RubyThread
5: 50581 3641832 java.util.WeakHashMap$Entry
6: 29514 3550112
7: 25001 3400136 org.jruby.runtime.ThreadContext
8: 3537 3073384
9: 3537 2986912
10: 25001 2600184 [Lorg.jruby.runtime.Frame;
11: 25001 2600184 [Lorg.jruby.runtime.DynamicScope;
12: 25001 2600184 [Lorg.jruby.RubyModule;
13: 25001 2600104 [Lorg.jruby.RubyKernel$CatchTarget;
14: 3196 2439120 [I
15: 44920 2277280
16: 25132 2010560 org.jruby.parser.LocalStaticScope
17: 3378 1882272
I think I’m seeing the behaviour noted in JRUBY-3742, that adopted
threads aren’t automatically unregistered. Adding these lines to a
finally block in in the thread’s run method seems to do enough that
things get cleaned up properly:
RubyThread rt = (RubyThread)
runtime.getThreadService().getRubyThreadMap().get(Thread.currentThread());
runtime.getThreadService().unregisterThread(rt);
This does a bit more than is discussed in the JIRA, so I wonder if
it’d be reasonable to have a utility method somewhere that you can
call to say “I’m done with JRuby from this thread (for now)”?
I’m trying this out in JRuby-Rack, and it seems to keep the leakage
under control. I’ll report back after it’s been running a while, but
it looks like something like this is necessary for the
message-handling path, and possibly for the web-request path too.
Cheers,
Chris.
To unsubscribe from this list, please visit:
http://xircles.codehaus.org/manage_email