Classloader does not collect garbage, even without a GC

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
  • ...

cl-gc

= 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
  • ...

cl-nogc

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

Thanks and best regards

+6
source share
3 answers

Finally, I can close this error because it seems to be related to OpenJPA and non-parameterized requests. Another thread to look at: Custom ClassLoader, not a garbage collector

+1
source

Without fragments of CustomClassLoader source code or actual heap dumps, it will be very difficult to track down the problem. Your CustomClassLoader cannot be a single. If so, your design may not work (or I missed something).

You need to get a list of ClassLoader instances of type CustomClassLoader and track links to these objects.

These messages can help you in how to analyze it further and get into the dark details of ClassLoader leak search:

+6
source

Garbage collection of class loaders is an extremely complex business. Using JProfiler , I see the following chain of inbound links to the currently active user class loader:

enter image description here

This shows that you have a static "singleInstance" field in your custom classloader that references the classloader. You should try to clear this field when redistributing in order to simplify the virtual machine for collecting the class loader.

A note on your result with the Eclipse MAT: it deletes all objects that do not reach the goal. JProfiler also does this by default. Thus, the three previous class loaders must be garbage collected, but they are not the result of special rules that the JVM has for the GC class loader, which are not fixed by standard references on the heap.

Disclaimer: my company is developing JProfiler

+2
source

All Articles