Bug 58296

Summary: Possible memory leak in JspRuntimeContext
Product: Tomcat 8 Reporter: Christoph Empl <christoph.empl>
Component: JasperAssignee: Tomcat Developers Mailing List <dev>
Severity: major CC: sherold.dev, therob256+asf
Priority: P2    
Version: 8.0.26   
Target Milestone: ----   
Hardware: PC   
OS: All   
Attachments: war to reproduce the bug
Chain with previous linked FastRemovalDequeue$Entry instances - Eclipse MAT Screenshot
JspRuntimeContext: retained heap contains invalid entry

Description Christoph Empl 2015-08-28 09:53:00 UTC
Limitation of maxLoadedJsps and usage of tag files may result in a memory leak in JspRuntimeContext.

Steps to reproduce the leak:

1) Set Tomcat in its web.xml to production mode and limit the max jsp count to 2

2) deploy the attach WAR-file "tomcat-test.war". The WAR contains one HttpServlet, three jsps and one tag file. GET-Requests to http://localhost:8080/tomcat-test/test render the jsps in the sequence "1.jsp" -> "2.jsp" -> "3.jsp". 
JSPs 1.jsp and 3.jsp use the tag "test.tag", 2.jsp contains only html.

3) make at least 5 GET-Requests to http://localhost:8080/tomcat-test/test

4) take a heap dump

5) open the dump with Eclipse MAT

6) execute the OOQL-Query 
select * from org.apache.jasper.util.FastRemovalDequeue$Entry e where e.valid=false

7) "Path To GC Roots" (right click on one result) shows that the invalid entries are in the retained heap of the tag's JspServletWrapper and hence in the retained heap of JspRuntimeContext.

The retained heap of JspRuntimeContext grows with every unloaded Jsp (its Entry in the jspQueue).

The bug is reproducible with Tomcat 7.0.52 and 8.0.26.

Supposed causes:

1) FastRemovalDequeue: removed Entry-instances are flagged as invalid. The "previous"-field of removed elements isn't set to null. So removed elements still reference their "previous" element (which still may be in the jspQueue of JspRuntimeContext)

2) JspServletWrappers of currently loaded jsps and tag files are stored in the ConcurrentHashMap "jsps" in JspRuntimeContext. JspServletWrappers of Tag files hold (indirectly over JspCompilationContext -> JasperTagInfo -> ImplicitTagLibraryInfo -> ParserController) references to JspServletWrapper instances with already removed "unloadHandles" (FastRemovalDequeue$Entry instances with valid == false). 

2) leads in combination with 1) to a reproducible memory leak: the chain of "previous" linked entries contains all JspServletWrappers of unloaded and currently loaded jsps. In combination with maxLoadedJsps != -1, the chain can grow till an OutOfMemoryError occurs.
Comment 1 Christoph Empl 2015-08-28 09:53:58 UTC
Created attachment 33047 [details]
war to reproduce the bug
Comment 2 Christoph Empl 2015-08-28 09:55:17 UTC
Created attachment 33048 [details]
Chain with previous linked FastRemovalDequeue$Entry instances - Eclipse MAT Screenshot
Comment 3 Christoph Empl 2015-08-28 10:03:06 UTC
Created attachment 33049 [details]
JspRuntimeContext: retained heap contains invalid entry
Comment 4 Mark Thomas 2015-09-07 11:43:58 UTC
Thanks for the report. We really do appreciate bug reports that are as well written as this one.

Your analysis was spot on. The bug has been fixed in trunk, 8.0.x (for 8.0.27 onwards) and 7.0.x (for 7.0.65 onwards).