Closed Bug 281067 Opened 20 years ago Closed 20 years ago

ThreadLocal in Context prevents class unloading

Categories

(Rhino Graveyard :: Core, defect)

defect
Not set
normal

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: szegedia, Assigned: igor)

Details

Attachments

(1 file)

User-Agent:       Opera/7.54 (Windows NT 5.0; U)  [en]
Build Identifier: 

I have an environment where most of the system (including Rhino) is hot-
reloadable. The classes are loaded through a particular java.net.URLClassLoader, 
and upon detecting a change, the class loader is thrown away, and a new one is 
created, allowing for the classes to be reloaded. However, a small issue in 
Rhino's Context class prevents the throwaway class loader (and all the classes 
it loads) from being garbage collected, resulting in memory leak over time.

The issue is the ThreadLocal "Context[] storage". Since the type of the value in 
ThreadLocal is org.mozilla.javascript.Context[], the class org.mozilla.
javascript.Context will remain permanently loaded, and will keep the class 
loader from being garbage collected -- we're utilizing a permanent set of 
threads, actually they're created by JMS sessions, so we can't just terminate 
and restart the threads to get rid of the stuck thread locals. However, there is 
a brilliantly simple solution - store an Object[] instead of Context[] in the 
thread local. Granted, we sacrifice a bit of static type safety, but Rhino will 
no longer prevent the class loader that loaded it from being garbage collected.
Patch on the way.

Reproducible: Always

Steps to Reproduce:
Patch attached
I thought that Java used week references to store ThreadLocal in Thread
instances. Apparently it does not which also means that even with the patch
after Rhino reloading all those ThreaLocal instances from reloaded Rhino classes
would stuck in memory forever.

Of cause it is much better then forever stuck Rhino classes themselves so I will
commit the patch after regression testing. 
Status: NEW → ASSIGNED
I committed the patch. 

BTW, since you repeatedly reload Rhino classes, did the patch address memory
leak completely on your JVM? Or do you still see some leaks like the one
described in 
https://bugzilla.mozilla.org/show_bug.cgi?id=243305#c9 ?
Status: ASSIGNED → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
(In reply to comment #3)
> 
> BTW, since you repeatedly reload Rhino classes, did the patch address memory
> leak completely on your JVM? Or do you still see some leaks like the one
> described in 
> https://bugzilla.mozilla.org/show_bug.cgi?id=243305#c9 ?

I'll run it through a profiler now (been profiling all day :-) ). However, what 
you say is certainly true - the ThreadLocal object and the Object[] will get 
stuck in the thread's local table... Hm... maybe in the end I'll really have to 
introduce a feature for perodically shutting down and recreating the JMS 
sessions...
(In reply to comment #3)
> I committed the patch. 
> 
> BTW, since you repeatedly reload Rhino classes, did the patch address memory
> leak completely on your JVM? Or do you still see some leaks like the one
> described in 
> https://bugzilla.mozilla.org/show_bug.cgi?id=243305#c9 ?

No, I see no leaks now. As a matter of fact, Thread indeed holds the ThreadLocal 
objects using weak references. However, weak references are defeated if the 
value - directly or indirectly - refers back strongly to the key. In the case 
with storing a Context[], we had a strong reference from value to key via the 
path value = Context[] instance -> Context[].class -> Context.class -> Context.
threadLocalCx = key. Storing an Object[] instead of Context[] solved it for good 
:-). Provided the array element is null (which is easy by using a Context.exit() 
in a finally block), the ThreadLocal object can be fully garbage collected after 
the Context class is unloaded.
Thinking about it, I believe it'd be necessary to provide a comment around 
"storage = new Object[1];" explaining why do we use it instead of Context[] - 
it's not immediately apparent, and some otherwise well-meaning soul (who comes 
to maintain it sometime in the future after we all moved along) unsuspectingly 
rewrites it back to Context[]...
(In reply to comment #5)
> As a matter of fact, Thread indeed holds the ThreadLocal 
> objects using weak references. However, weak references are defeated if the 
> value - directly or indirectly - refers back strongly to the key. In the case 
> with storing a Context[], we had a strong reference from value to key via the 
> path value = Context[] instance -> Context[].class -> Context.class -> Context.
> threadLocalCx = key.


But why the reference from Context.threadLocalCx prevents in your case Rhino
classes from garbage collection? If ThreadLocal value is reachable from Thread
only through week reference, it should allow to GC classes as long as the last
strong reference to them and their class loader is gone.
(In reply to comment #7)
> 
> But why the reference from Context.threadLocalCx prevents in your case Rhino
> classes from garbage collection? If ThreadLocal value is reachable from Thread
> only through week reference, it should allow to GC classes as long as the last
> strong reference to them and their class loader is gone.

No, only the keys in the WeakHashMap are weakly reachable (in our case, the 
ThreadLocal object). The values in WeakHashMap are strongly reachable. And if 
the value is a Context[], it'll establish a strong reference to the ThreadLocal 
key, as outlined in comment #5, preventing it to be GC-ed for the lifetime of 
the thread.
(In reply to comment #8)
>... only the keys in the WeakHashMap are weakly reachable (in our case, the 
> ThreadLocal object). The values in WeakHashMap are strongly reachable. And if 
> the value is a Context[], it'll establish a strong reference to the ThreadLocal 
> key, as outlined in comment #5, preventing it to be GC-ed for the lifetime of 
> the thread.

I will add then comments about using Object[], not Context[] after I will finish
with bug 281247.
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: