https://bz.apache.org/bugzilla/show_bug.cgi?id=58023

            Bug ID: 58023
           Summary: Memory Leak in WebappClassLoader
           Product: Tomcat 7
           Version: 7.0.59
          Hardware: PC
            Status: NEW
          Severity: normal
          Priority: P2
         Component: Catalina
          Assignee: dev@tomcat.apache.org
          Reporter: m...@interact.com.br

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;
    }
}

-- 
You are receiving this mail because:
You are the assignee for the bug.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to