package org.foo.leaky;
import org.osgi.framework.*;
/**
* To run this example, first build it with ant then type:
*
* java -verbose:gc -jar launcher.jar bundles
*
* You should see the example start with GC trace enabled:
*
* [GC 896K->136K(5056K), 0.0029125 secs]
* ...
* ->
*
* If you try to update the bundle you should see a leak:
*
* [Full GC 320K->320K(5056K), 0.0280867 secs]
* -> update 1
* [GC 16979K->16717K(21444K), 0.0014941 secs]
* [Full GC 16717K->16714K(21444K), 0.0256443 secs]
* -> update 1
* [GC 33378K->33118K(46424K), 0.0009662 secs]
* [Full GC 33118K->33118K(46424K), 0.0268457 secs]
* -> update 1
* [GC 49777K->49514K(59424K), 0.0009663 secs]
* [Full GC 49514K->49463K(59424K), 0.0929226 secs]
* [Full GC 49463K->49452K(65088K), 0.0452441 secs]
* java.lang.OutOfMemoryError: Java heap space
* ->
*
* Uncomment the call to remove(), rebuild and try again:
*
* [Full GC 320K->320K(5056K), 0.0238252 secs]
* -> update 1
* [GC 16973K->16717K(21444K), 0.0021946 secs]
* [Full GC 16717K->16717K(21444K), 0.0296025 secs]
* -> update 1
* [GC 33373K->33114K(46428K), 0.0010646 secs]
* [Full GC[Unloading class org.foo.leaky.Activator$Data]
* [Unloading class org.foo.leaky.Activator$1]
* [Unloading class org.foo.leaky.Activator]
* 33114K->16721K(46428K), 0.0557759 secs]
*
* The leak is now gone and the classes are unloaded.
*/
public class Activator implements BundleActivator {
/*
* 8Mb data object
*/
static class Data {
StringBuffer data = new StringBuffer(8 * 1024 * 1024);
}
/*
* This example relies on a "feature" of the Java5 ThreadLocal implementation
* where stale map entries are only cleared if set() or remove() is called on
* another ThreadLocal for the same thread - and in the worst case even this
* is not guaranteed to purge all stale map entries.
*
* As we shall soon see, missing out the remove() call in stop means that the
* data object will be kept alive indefinitely because we don't use any other
* ThreadLocal in our example. This in turn keeps our ClassLoader alive.
*
* Calling remove() in stop forces the underlying map entry to be cleared and
* means the bundle's ClassLoader can now be collected on each update/refresh.
*/
static final ThreadLocal leak = new ThreadLocal() {
protected Object initialValue() {
return new Data();
};
};
public void start(BundleContext ctx) {
leak.get();
}
public void stop(BundleContext ctx) {
// leak.remove();
}
}