We have a sophisticated application running under Glassfish V2.1.1. To dynamically load our code, we implemented CustomClassloader, which can override classes. The behavior is quite simple: when a dynamically loaded class changes, the current instance of CustomClassloader is “discarded”, and a new one is created to override the necessary classes.
This works well, except that after several times the same class was reloaded (therefore, every time a new CustomClassloader is created), we get a PermGen spatial error, because other instances of CustomClassloader are not garbage collected. (There must be only one instance of this class)
I tried different methods to track leak:
- visualvm => I do a bunch of dumps and retrieve all instances of CustomClassloader. I see that none of them has been finalized. When I check the nearest GC root, visualvm tells me that it is not (except for the last instance, because it is "real").
- jmap / jhat => This gives me almost the same result: I see all instances of CustomClassloader, and then when I click on the link to see where the links to one of them are, I get a blank page, which is not ...
- Eclipse Memory Analysis Tool => I get a strange result when I run the following OQL query:
SELECT c FROM INSTANCEOF my.package.CustomClassloader c There is only one result indicating that there is only one instance that is obviously not correct.
I also checked this link and implemented some resources when creating a new CustomClassloader, but nothing changed: PermGen's memory is still growing.
So, I probably missed something, and the difference between points (1-2) and (3) shows what I don't understand. Where can I take a look to understand what happened? Since all the training materials that I cited show how to search for link leaks using the "Find the closest GC root" function (and in my case it’s not there), I don’t know how I can track the error.
EDIT 1: I downloaded the heap heap example here . The Loader class, which is not unloaded, can be selected in visualvm with the following query: select s from saierp.core.framework.system.SAITaskClassLoader s You can see that there are 4 instances, and the first three must be assembled because there is no GC root. .. There should be a link, but I do not know how I can search for it. Any hints are welcome :)
EDIT 2: After some deeper tests, I see a very strange pattern. The leak seems to depend on the data that OpenJPA is loading: if new data is not loaded, then the class loader may be GCed, otherwise it is not. Here is the code that I use when I create a new SAITaskClassLoader to "clear" the old:
PCRegistry.deRegister(cl); LogFactory.release(cl); ResourceBundle.clearCache(cl); Introspector.flushCaches();
= Sample 1 (Classloader GCed): =
- New SAITaskClassLoader
- Data loading D1, D2, ..., Dn
- New SAITaskClassLoader
- Data loading D1, D2, ..., Dn
- ...

= Sample 2 (Classloader is NOT GCed): =
- New SAITaskClassLoader
- Loading data D1, D2, D3
- New SAITaskClassLoader
- Loading data D3, D4, D5
- New SAITaskClassLoader
- Loading data D5, D6, D7
- ...

In all cases, the cleared SAITaskClassLoader does not have a GC root. We are using OpenJPA 1.2.1.
Thanks and best regards