Bug 49176 - Jasper in Dev Mode Is Memory Inefficient
Summary: Jasper in Dev Mode Is Memory Inefficient
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 6
Classification: Unclassified
Component: Jasper (show other bugs)
Version: 6.0.24
Hardware: All All
: P2 enhancement (vote)
Target Milestone: default
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-04-23 09:31 UTC by Scott Hamilton
Modified: 2017-06-28 20:03 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Scott Hamilton 2010-04-23 09:31:49 UTC
org.apache.jasper.compiler.Compiler.compile(), ~line 357 has this code:

            // Only get rid of the pageNodes if in production.
            // In development mode, they are used for detailed
            // error messages.
            // http://issues.apache.org/bugzilla/show_bug.cgi?id=37062
            if (!this.options.getDevelopment()) {
                pageNodes = null;
            }

Turns out this has a pretty substantial impact on the heap usage for JSPs.  Our application has ~2300 JSPs, which if all are compiled and loaded when Jasper is in production mode yields roughly an 80m overhead.  However, if all are compiled and loaded while Jasper is in development mode, the overhead is ~1.8g - yes, that's gigabytes.

For most development environments this won't be an issue b/c all of the JSPs won't need to be compiled and loaded, and one would assume that in production you turn off development mode.

However, for reasons outside the scope of this bugzilla issue, we can't turn this off in production.

However, it seems like a very small change might result in big big savings for those who might also be like us, plus also just raw savings in development mode.

If my analysis is correct (and I'm all ears to hear that it is not), the pageNodes data is really only used after compilation if a JSP compile error is enountered.  The pageNodes data will be picked up and used in generating a more detailed error page.

So... can the Compiler.compile() method ALWAYS clear this out if there is no exception in the compilation?  This would give the best of both worlds - huge memory savings plus detailed error pages.

Final note: to prove this out, we wrapped the JspServlet in our own servlet so which basically just proxies the Jasper JspServlet EXCEPT that when the JspServlet.service() method completes, we check to see if the JSP was just compiled, and if it was, we reflectively set the pageNodes property of the associated Compiler to null.
Comment 1 Mark Thomas 2010-04-23 09:36:54 UTC
The pageNodes are required to generate detailed error messages for runtime (eg NPE) errors as well as compile time errors.
Comment 2 Scott Hamilton 2010-04-23 13:14:01 UTC
That's a good point - indeed I missed that in looking thru the code.

I wonder, though, if there's any optimization that could be done here to reduce the memory footprint of this pageNodes data structure, however.  As I indicated, it does seem to be quite expensive.

Another alternative might be to perhaps temporarily reconstruct the pageNodes when an exception occurs and this information is to be displayed.  Granted, you'd incur a performance hit, but not much of one (Jasper is pretty fast these days) and we're talking about this only in development mode anyway...
Comment 3 Scott Hamilton 2010-04-23 14:03:18 UTC
I don't mean to belabor the point, but I also noticed that the usage of this data for runtime exceptions is not quite consistent.  It will only be used and show up in error messages on a JSP that is compiled, but if the server has started up and the already compiled .class file is not out of date, Jasper will load the class without reconstructing this pageNodes data, and your subsequent error messages will be devoid of this info.

Maybe you could consider that a different bug if indeed the intent here is for when Jasper is in dev mode that errors both compile-time and runtime should show this extra pageNodes data.
Comment 4 Mark Thomas 2010-04-23 19:37:10 UTC
Not having the page nodes after a restart is probably a separate issue (although one worth keeping in mind whilst thinking about this issue).

Keep in mind that pageNode generation is expensive, relative to serving a response. Not caching the pageNodes is likely to open up a DOS attack vector.

A theoretical (as in I haven't looked at the code to see if it is feasible or how much work it would be) is to ignore the pageNodes and use the SMAP. The SMAP could either be cached (should be much smaller than pageNodes) or read when required. Since it is saved in the .class file it would also survive a restart. I'd be tempted to cache it for the life of the JVM on compilation and read it from disk as required after a restart.

I'm re-opening this and marking it as a (performance) enhancement.

It will get looked at eventually, but enhancements tend to be at the bottom of people's todo list. If you wanted to provide a patch...
Comment 5 Mark Thomas 2017-06-28 20:03:15 UTC
This has been fixed in trunk for 9.0.M23 onwards.

Because it required quite a few API changes in Jasper, I do not propose to back-port this.