Hi Mike,
Good stuff - we need to clean it up. I was not aware of the issue
with Configuration shutdown hook. Looking at the code, there is no
excuse for creating it eagerly for every instance. Let me actually
fix it now.
Regarding EventManager, this is a known issue. There is an
EventManager.shutdown() method added in 3.0 (CAY-610) and we are
planning to weed out a few remaining references to the default static
EM (CAY-868) that leaks just a few threads.
Andrus
On Oct 18, 2007, at 10:41 PM, Mike Kienenberger wrote:
For what it's worth, there's a memory leak in Configuration. I doubt
it will affect many people, but it was a significant contributor to
the 1.5Gb memory requirements and 35 minutes when running my tests
(now down to 400Mb in 25 minutes, although Velocity, StrutsTest, and
personal memory leaks contributed some to this issue as well).
The problem is that "new Thread()" without "thread.start()" under Java
1.4 leaks memory.
Every new instance of Configuration creates a new Thread() without
starting it, and since this is an inner class for Configuration,
configuration leaks. And that pretty much leaks the entire Cayenne
runtime.
All of my Cayenne tests create a new configuration, so this was a
problem for me.
Here's how I solved it in case anyone else has the same situation. I
subclassed DefaultConfiguration (which is the superclass for all of my
other classes) as follows, and then called the "clear()" method in my
teardown().
public class CleanableDefaultConfiguration extends
DefaultConfiguration
{
public CleanableDefaultConfiguration()
{
super();
}
public CleanableDefaultConfiguration(String
domainConfigurationName)
{
super(domainConfigurationName);
}
public void clear() {
uninstallConfigurationShutdownHook();
((Thread)configurationShutdownHook).start();
try {
((Thread)configurationShutdownHook).join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
configurationShutdownHook = null;
shutdown();
this.setResourceLocator(null);
sharedConfiguration = null;
}
}
I also found issues with EventManager. There's no public interface
to reset it. Here's what I ended up doing, but at some point there
probably should be a way made to handle this with standard java code,
and also shut down the EventManager.Dispatch threads. Currently
those threads run until the JVM dies, and since they are inner classes
to EventManager, that means EventManager and its referents can't be
garbage collected.
What I did was used reflection to clear out the subjects and
eventQueue fields, which cuts down on the impact of the leak.
// defaultManager is final -- can't reset it in java 1.4.
EventManager defaultManager = EventManager.getDefaultManager
();
Field eventManagersubjectsField =
EventManager.class.getDeclaredField("subjects");
eventManagersubjectsField.setAccessible(true);
eventManagersubjectsField.set(defaultManager,
Collections.synchronizedMap(new WeakHashMap()));
Field eventManagereventQueueField =
EventManager.class.getDeclaredField("eventQueue");
eventManagereventQueueField.setAccessible(true);
eventManagereventQueueField.set(defaultManager,
Collections.synchronizedList(new LinkedList()));
Again, probably not an issue for most use cases, but with 800+
integration tests, it's an issue for me.