Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Performance Impact of Undefined Variables in EL 3.0

DZone's Guide to

Performance Impact of Undefined Variables in EL 3.0

Tomcat 8 implements the EL 3.0 Specs. One of the added features is “Static Field and Method Reference”. In this post we'll evaluate why this change is triggering a slow down and how to improve the performance.

Free Resource

Introduction

Tomcat 8 implements the EL 3.0 Specs. One of the added features is “Static Field and Method Reference”. This feature evaluates expressions like ${Boolean.TRUE}.

In some circumstances, this change triggers significant slow down. (Migration Guide Tomcat 8 )

When using JSPs in Tomcat 8, we strongly recommend using requestScope when using variables in JSPs. Not doing so can result in bad performance! This may even cause your site to go down.

( Hippo Documentation: USING TOMCAT 8)

This blog post:

  • Shows an experiment where this slow down can be observed clearly
  • Suggests ways to improve the performance
  • Provides background why this slow down occurs; and
  • Suggests some ways to find out that “Static Field and Method Reference” is actually causing the slow down.

Experiment

In this experiment two Hippo components (could essentially be servlets as well) are compared. Both set 200 request attributes. One component fills them with null the other one with a random value:

UUID.randomUUID().toString()

The experiment compares the performance with performance tests created in Gatling. See the demo project on github

200 Defined Variables (These Values Look Good):

Performance with defined variables

200 Undefined variables (These Clearly Don’t):

Performance with undefined variables

Solution

Possible solutions are:

  • Provide scope to attributes that could possible be undefined;
  • Make sure attributes cannot be undefined; or
  • Use the parallel class loader.

There are three ways a variable in an el expression could be undefined:

c:set

A variable will be undefined if in the following snippet copyright is null. The variable will have page scope by default.

<c:set var="copyright"value="${document.copyright}"/>

setAttribute

A variable can also be undefined if in the following snippet document is null. The variable will have request scope by default.

...
request.setAttribute("attribute1",document);
...

Attributes of Custom jsp Tags

At last (easy to miss) a variable can be undefined if an attribute of a custom tag is set to null. E.g. when in the following snippet document is null. The variable will have page scope by default.

<hippo-gogreen:copyrightdocument="${document}"/>
<%@attributename="document"
type="com.onehippo.gogreen.beans.BaseDocument"
rtexprvalue="true"
required="false"%>

Provide Scope

One solution is to scope all variables that could possibly be “undefined” in any EL expression as described in the Migration Guide of Tomcat 8.

<c:setvar="undefined"value="${null}"scope="page"/>
<c:iftest="${pageScope.undefined}">
....
</c:if>

Downside is that if you use elvariables in your IDE like:

<%--@elvariableid="document"type="com.onehippo.gogreen.beans.NewsDocument"--%>

The autocomplete functionality will be lost.

Prevent Nullability

Another solution is to make sure that variables cannot be null. One solution might be to use the Optional introduced in Java 8.

Use the Parallel ClassLoader

Use the Parallel ClassLoader by adding the following element to the context element in your context.xml:

<LoaderloaderClass="org.apache.catalina.loader.ParallelWebappClassLoader"/>

See the Parallel Classloader branch in the demo project.

Analysis

How do you know your site has slowed down because of this issue ? Most probably you’ll see loads of others warning that are caused by an inaccessable classloader and the number of concurrent requests ( The number of concurrent users rapidly increases ).

Stackdump

  • Create a stackdump using jstack: jstack [pid] > dump
  • investigate the dump. If you find all threads waiting for the classloader thread, it’s very like caused by this problem.

Analysis Tools

  • Use an analysis tool like XRebel or in the case of Hippo switch on page diagnostics. It will give a indication of which jsp’s / tag files are causing the problem.

Find Instances of Undefined Variables at Runtime

Logging

To find instances of undefined variables at runtime, one could add an additional ElResolver. This ELResolver only reports if a variable is undefined at runtime. In order to see this in action:

For every undefined variable you’ll now see in the logs:

Variable with name:requestAttribute197 is not defined, 
trying to look name on classpath

Debugging

Now you can also add a breakpoint at LoggingUndefinedVariableReporter#report. In combination with jsp debugging this will make you spot all instances of this problem very quickly.

Background

Each time an expression cannot be evaluated within session, request or page scope or cannot be evaluated as the name of an imported class, it will try to lookup the variable as a name of a class on the classpath. This causes slowdown, because all threads are waiting for the classloader thread.

See javax.servlet.jsp.el.ScopedAttributeELResolver.getValue()

 @Override
43     public Object More ...getValue(ELContext context, Object base, Object property) {
44         if (context == null) {
45             throw new NullPointerException();
46         }
47 
48         Object result = null;
49 
50         if (base == null) {
51             context.setPropertyResolved(base, property);
52             if (property != null) {
53                 String key = property.toString();
54                 PageContext page = (PageContext) context
55                         .getContext(JspContext.class);
56                 result = page.findAttribute(key);
57 
58                 if (result == null) {
59                     // This might be the name of an imported class
60                     ImportHandler importHandler = context.getImportHandler();
61                     if (importHandler != null) {
62                         Class<?> clazz = importHandler.resolveClass(key);
63                         if (clazz != null) {
64                             result = new ELClass(clazz);
65                         }
66                         if (result == null) {
67                             // This might be the name of an imported static field
68                             clazz = importHandler.resolveStatic(key);
69                             if (clazz != null) {
70                                 try {
71                                     result = clazz.getField(key).get(null);
72                                 } catch (IllegalArgumentException | IllegalAccessException |
73                                         NoSuchFieldException | SecurityException e) {
74                                     // Most (all?) of these should have been
75                                     // prevented by the checks when the import
76                                     // was defined.
77                                 }
78                             }
79                         }
80                     }
81                 }
82             }
83         }
84 
85         return result;
86     }
Topics:
tomcat 8

Published at DZone with permission of Michiel Rop. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}