glassfish
  1. glassfish
  2. GLASSFISH-3963

Tag-file classes get loaded for each JSP leading to PermGen space problem

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Minor Minor
    • Resolution: Unresolved
    • Affects Version/s: 9.1peur1
    • Fix Version/s: not determined
    • Component/s: web_container
    • Labels:
      None
    • 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() {

        Activity

        kmeduri created issue -
        Hide
        jluehe added a comment -

        Adding Kin-Man to interest list.

        Show
        jluehe added a comment - Adding Kin-Man to interest list.
        Hide
        harpreet added a comment -

        Approving for v2.1

        Show
        harpreet added a comment - Approving for v2.1
        Hide
        jluehe added a comment -

        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.

        Show
        jluehe added a comment - 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.
        Hide
        sanandal added a comment -

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

        Show
        sanandal added a comment - "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."
        kenaiadmin made changes -
        Field Original Value New Value
        issue.field.bugzillaimportkey 3963 35567
        Hide
        Tom Mueller added a comment -

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

        Show
        Tom Mueller added a comment - Bulk update to change fix version to "not determined" for all issues still open but with a fix version for a released version.
        Tom Mueller made changes -
        Fix Version/s not determined [ 11149 ]
        Fix Version/s V3 [ 10981 ]

          People

          • Assignee:
            kmeduri
            Reporter:
            kmeduri
          • Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated: