Memory leak occur because a reference of entry.manifest is never removed. Problem: 1 - ${class}.class.getResource( ${resource.name1} ); 2 - JarFile load a manifest. 3- resourceEntries put a new ResourceEntry with a manifest reference. 4 - ${class}.class.getResource( ${resource.name2} ); 5 - resourceEntries put a new ResourceEntry with a manifest reference. Manifest contains ~20MB, but, no problem because the JarFile is the same reference. In other words, only one Manifest in the Heap space. 6 - wait for 90000 milliseconds. 7 - ${class}.class.getResource( ${resource.name3} ); 8 - WebappClassLoader.closeJARs because time is elapsed 90000 milliseconds and load a new JarFile instances. 9 - JarFile load a new manifest. 10 - resourceEntries put a new ResourceEntry with a new manifest reference. 11 - wait for 90000 milliseconds ... And this will be memory leak in little time (See resourceEntries.png). In attachment, a scenario to simulate this problem. PS.: A jar file need to be signed for accelerate leak, because of Manifest.entries ~5MB (see manifests.png) retained heap (in test case, but, my real app is ~20MB). I attached the heap dump used in this test case (heap.zip). Solution: Always release the manifest reference. Workaround: public class WebappClassLoader extends org.apache.catalina.loader.WebappClassLoader { @Override public InputStream getResourceAsStream( String name ) { InputStream in = super.getResourceAsStream( name ); ResourceEntry entry = resourceEntries.get( name ); if ( entry != null ) { // prevent a memory leak entry.manifest = null; entry.certificates = null; } return in; } @Override public URL getResource( String name ) { URL url = super.getResource( name ); ResourceEntry entry = resourceEntries.get( name ); if ( entry != null ) { // prevent a memory leak entry.manifest = null; entry.certificates = null; } return url; } }
Thanks for the report. Strictly I would class this as excessive memory usage rather than a leak since a) there is an upper limit to the memory that will be used and b) the memory will be recovered once the application has been undeployed. Regardless of how it is classified, it is a bug. I have applied an alternative fix (only request and retain the manifest while loading a class and then drop it) that should achieve the same ends for trunk (9.0.x), 8.0.x (for 8.0.24 onwards) and 7.0.x (for 7.0.63 onwards).