Java Reference
In-Depth Information
Listing 8.6 Leaky bundle activator
public class Activator implements BundleActivator {
static class Data {
StringBuffer data = new StringBuffer(8 * 1024 * 1024)
}
static final ThreadLocal leak = new ThreadLocal() {
protected Object initialValue() {
return new Data();
};
};
public void start(BundleContext ctx) {
leak.get();
}
public void stop(BundleContext ctx) {}
}
Each leaked
ThreadLocal
takes up a noticeable 8
MB
. Following recommended prac-
tice, the
ThreadLocal
is a static member of the class. This is safe because the
JVM
guar-
antees to supply a distinct instance of the data object for each thread accessing the
ThreadLocal
. But how does forgetting to remove the
ThreadLocal
cause a memory
leak? If you read the
ThreadLocal
Javadoc, you may expect the
JVM
to clear up stale
Each thread holds an implicit reference to its copy of a thread-local variable as long as
the thread is alive and the ThreadLocal instance is accessible; after a thread goes away,
all of its copies of thread-local instances are subject to garbage collection (unless other
references to these copies exist).
If the bundle has been updated and the framework refreshed, surely the stale data
object is no longer accessible and should be removed, right? Unfortunately, the Java 5
ThreadLocal
implementation has a subtle behavior that causes it to hang on to values
longer than is strictly necessary.
ThreadLocal
behavior in Java 5
The Java 5
ThreadLocal
implementation only clears stale thread-local map entries
if
set()
or
remove()
is called on
another
ThreadLocal
for the
same thread
. In the
worst case, even this isn't guaranteed to purge all stale thread-local map entries.
As you'll soon see, missing the
remove()
call in
stop()
means that the data object is
kept alive indefinitely because you don't use any other
ThreadLocal
s in the example.
This in turn keeps your class loader alive. Let's see this leak in action:
$
cd chapter08/memory-leaks
$
ant dist
$
java -verbose:gc -jar launcher.jar bundles