[GLASSFISH-3963] Tag-file classes get loaded for each JSP leading to PermGen space problem Created: 04/Jan/08  Updated: 06/Mar/12

Status: Open
Project: glassfish
Component/s: web_container
Affects Version/s: 9.1peur1
Fix Version/s: not determined

Type: Bug Priority: Minor
Reporter: kmeduri Assignee: kmeduri
Resolution: Unresolved Votes: 1
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

Operating System: All
Platform: All


Issuezilla Id: 3,963

 Description   

Here is a problem described on apache tomcat dev list. GF also suffers from it.

Problem:
========
The class generated from the tag file gets loaded for
every JSP the tag is placed on. So if you put such a tag on a few thousand
JPSs, the tag class will seriously pollute the perm gen space. The
problem gets even worse, if you have a great number of tags used nearly
on every singe page. (You can trace class loading by starting the VM
with the options "-XX:+TraceClassLoading -XX:+TraceClassUnloading" and
see).

Reason:
=======
A new instance of a JspCompilationContext is used for every JSP. This
JspCompilationContext holds a reference to a JasperLoader (which is null
in the beginning and a new instance is created by the
JspCompilationContext on demand). Thus a new JasperLoader (a new
ClassLoader) is used for every JSP. As tagfiles of tags placed on this
JSP get loaded via the same ClassLoader as the JSP, the class of the
same tag placed on different JSPs gets loaded via different
ClassLoaders.

There is no problem with Java-coded tags, as those get loaded by the
parent class loader and the JasperLoaders delegate to it's parent if the
class-name does not start with "org.apache.jsp".

Loading each JSP via a different ClassLoader is necessary if you want to be able
to reload the JSP-class, but if the reloading-feature is turned off
(development=false), there is no need for a new ClassLoader for each JSP/tag.

Related apache tomcat bug:
http://issues.apache.org/bugzilla/show_bug.cgi?id=43878

Steps to reproduce:
===================
1. Create firstTag.tag as shown below under <anyapp>/WEB-INF/tags:

firstTag.tag
------------
<%@ tag import="java.util.Date"
import="java.text.DateFormat"%>
<%
DateFormat dateFormat =
DateFormat.getDateInstance(DateFormat.LONG);
Date now = new Date(System.currentTimeMillis());
out.println(dateFormat.format(now));
%>
------------

2. Create 1.jsp, 2.jsp as shown below under <anyapp> directory.

1.jsp:
------
<%@ taglib prefix="easy"
tagdir="/WEB-INF/tags" %>
In 1.jsp: Today is <easy:firstTag/>
------

2.jsp:
------
<%@ taglib prefix="easy"
tagdir="/WEB-INF/tags" %>
In 2.jsp: Today is <easy:firstTag/>
------

3) Set the JVM option "-XX:+TraceClassLoading -XX:+TraceClassUnloading" in
domain.xml and access 1.jsp and 2.jsp.

You will see that the the firstTag_tag class is loaded for each JSP the tag is
placed on. This is true even when the 'development' is set to 'false' and/or
'reload-interval' is set to '-1' in default-web.xml.

Suggested fix for PWC1.2 branch:
================================

Index: org/apache/jasper/JspCompilationContext.java
===================================================================
RCS file:
/cvs/glassfish/appserv-webtier/src/java/org/apache/jasper/JspCompilationContext.java,v
retrieving revision 1.9.6.4
diff -u -r1.9.6.4 JspCompilationContext.java
— org/apache/jasper/JspCompilationContext.java 27 Mar 2007 17:23:37 -0000 1.9.6.4
+++ org/apache/jasper/JspCompilationContext.java 4 Jan 2008 00:45:29 -0000
@@ -638,11 +638,7 @@

public ClassLoader getJspLoader() {

  • return new JasperLoader(new URL[] {baseUrl},
    - getClassLoader(),
    - rctxt.getPermissionCollection(),
    - rctxt.getCodeSource(),
    - rctxt.getBytecodes());
    + return rctxt.getJspLoader(baseUrl, getClassLoader());
    }

    public void makeOutputDir() { Index: org/apache/jasper/compiler/JspRuntimeContext.java =================================================================== RCS file: /cvs/glassfish/appserv-webtier/src/java/org/apache/jasper/compiler/JspRuntimeContext.java,v retrieving revision 1.7.6.3 diff -u -r1.7.6.3 JspRuntimeContext.java --- org/apache/jasper/compiler/JspRuntimeContext.java 11 Jan 2007 08:39:04 -0000 1.7.6.3 +++ org/apache/jasper/compiler/JspRuntimeContext.java 4 Jan 2008 00:45:29 -0000 @@ -58,6 +58,7 @@ import org.apache.jasper.runtime.JspFactoryImpl; import org.apache.jasper.security.SecurityClassLoad; import org.apache.jasper.servlet.JspServletWrapper; +import org.apache.jasper.servlet.JasperLoader; /** * Class for tracking JSP compile time file dependencies when the @@ -193,6 +194,7 @@ private ServletContext context; private Options options; private URLClassLoader parentClassLoader; + private JasperLoader jspLoader; private PermissionCollection permissionCollection; private CodeSource codeSource; private String classpath; @@ -440,6 +442,23 @@ }

    /**
    + * Obtain the classloader to use when loading JSP resources. In development
    + * mode, each JSP has a separate classloader to enable easy re-loading of
    + * modified JSPs. If not in development mode, a single loader is used to
    + * reduce perm gen usage when many JSPs all use the same tags.
    + */
    + public URLClassLoader getJspLoader(URL baseUrl, ClassLoader parent) {
    + if (options.getDevelopment() || jspLoader == null) {
    + jspLoader = new JasperLoader(new URL[] {baseUrl}

    ,
    + parent,
    + permissionCollection,
    + codeSource,
    + bytecodes);
    + }
    + return jspLoader;
    + }
    +
    + /**

  • Method used to initialize classpath for compiles.
    */
    private void initClassPath() {


 Comments   
Comment by jluehe [ 04/Jan/08 ]

Adding Kin-Man to interest list.

Comment by harpreet [ 15/Apr/08 ]

Approving for v2.1

Comment by jluehe [ 16/Apr/08 ]

I have the following concerns about the proposed patch:

1. getJspLoader() now would need to be "synchronized", because
jspLoader may be null for one thread when another has just assigned
to it an instance of JasperLoader.
However, adding "synchronized" will have negative impact on performance.

2. Can getJspLoader() ever be called with different baseUrl and parent
arguments? If so, imagine the following:

getJspLoader(<somebaseurl>, <someparent>)

followed by

getJspLoader(<someotherbaseurl>, <someotherparent>)

The 2nd call will now return the jspLoader created by the 1st call
that was using a different url and classloader.

I'm not sure if this scenario can happen, but from just looking at this
method, it seems like a problem.

Removing "911Approved" from status whiteboard field, because I think fixing this
bug for 9.1.1 would be too risky, considering the above concerns. Instead,
changing the target milestone to V3.

Comment by sanandal [ 11/Jan/09 ]

"Reclassifying as P4 because this issue is not deemed "must fix" for this v2.1
release whose primary release driver is SailFin.
This issue will be scrubbed after this release and will be given the right
priority for the next release."

Comment by Tom Mueller [ 06/Mar/12 ]

Bulk update to change fix version to "not determined" for all issues still open but with a fix version for a released version.

Generated at Sat Dec 03 03:01:12 UTC 2016 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.