-
Notifications
You must be signed in to change notification settings - Fork 40.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Switch to Apache EL implementation by default #24744
Comments
Thanks very much for the detailed analysis @TranceTip. In 2.3.0 we made a change meant that we use the same EL implementation (the RI) with all the embedded containers that we support (#19550). This is why the expression factory has changed. It may be that we can configure the RI to behave as the Apache EL implementation did, we'll need to look into that possibility. In the meantime, you should be able to exclude |
Confirmed, when adding
to the Note, however, that |
Both |
Thanks for trying the Apache EL implementation. The EL spec requires that
In this case, with two implementations on the classpath (each of which has a |
Thanks, I will then go with the exclusion for now. In any case, the dependency conflict regarding |
See https://bz.apache.org/bugzilla/show_bug.cgi?id=57583 for some background on the performance optimisation made in Tomcat's EL implementation.
We can consider excluding |
There are a couple of things to consider for this issue:
The need for 2 may change depending on what we decide to do for 1. |
FYI: In our web application on a test environment, the landing page now has a throughput which is 6 times faster than before with the exclusion in place, so we have our previous 2.2.x performance back. |
Will this fix be backported to 2.3.x and 2.4.x as well? |
@TranceTip this is flagged as an enhancement so, no it won't. |
My team had exactly the same issue after we migrated from spring boot 1.5.x to 2.3.6 and looks like the exclusion of I was googling for "spring boot jsp render slow" (and similar) quite often this month, but this thread never popped up. Writing this so that hopefully more people are able to find it. |
We had also faced the same problem and the exclusion of This leads us to more improved performance and moves away from any specific implementation. |
Ran into this problem yesterday and spent hours debugging. The slowdown in a (large) packaged spring boot app (with jsps utilizing jstl) is huge because the jars inside the war file are scanned when the class loader searches for the classes
At first glance I didn't understand this statement, but here's the long version: https://tomcat.apache.org/migration-8.html#JavaServer_Pages_2.3 |
This fix is in Spring Boot 2.5.0, right? Shouldn't it be listed on https://github.com/spring-projects/spring-boot/releases? |
@TranceTip it is listed in the 2.5.0-M1 release as well as the official release notes. |
…of org.glassfish:jakarta.el to be inline with spring (see spring-projects/spring-boot#24744 for details)
…of org.glassfish:jakarta.el to be inline with spring (see spring-projects/spring-boot#24744 for details)
…of org.glassfish:jakarta.el to be inline with spring (see spring-projects/spring-boot#24744 for details)
…of org.glassfish:jakarta.el to be inline with spring (see spring-projects/spring-boot#24744 for details)
…of org.glassfish:jakarta.el to be inline with spring (see spring-projects/spring-boot#24744 for details)
There is no need for this dependency. An el-impl is already provided by Spring Boot/tomcat-embed-el. tomcat-embed-el is also used for other servlet containers. Related: spring-projects/spring-boot#24744
There is no need for this dependency. An el-impl is already provided by Spring Boot/tomcat-embed-el. tomcat-embed-el is also used for other servlet containers. Related: spring-projects/spring-boot#24744
There is no need for this dependency. An el-impl is already provided by Spring Boot/tomcat-embed-el. tomcat-embed-el is also used for other servlet containers. Related: spring-projects/spring-boot#24744
There is no need for this dependency. An el-impl is already provided by Spring Boot/tomcat-embed-el. tomcat-embed-el is also used for other servlet containers. Related: spring-projects/spring-boot#24744
We have noticed that JSP EL expression evaluation has become significantly slower when non-existing attributes are accessed, starting with Spring Boot 2.3.0. This problem seems to affect all current versions of Spring Boot 2.3.x (2.3.0 to 2.3.7) and 2.4.x (2.4.0 and 2.4.1), but not 2.2.x.
Starting with Spring Boot 2.3.0, the following happens: For each non-existing attribute accessed during Spring EL expression evaluation on a JSP (for example, when using
${myProperty}
or<c:if test="${myProperty}">...</c:if>
and no such attribute is defined in the view model), there are class loader calls to find the classes "java.lang.<attribute>
", "java.servlet.<attribute>
", "java.servlet.http.<attribute>
" and "java.servlet.jsp.<attribute>
".This happens once per unique attribute on each request (or per JSP/tag context); accessing the same attribute again in the same context does not lead to additional class lookups. This significantly slows down the EL expression evaluation, since the 4 lookups for each attribute take a few milliseconds on an idle machine. When properties that do exist are accessed, there is no performance difference between the different Spring Boot versions. At least in our HTML frontend it is common that we only set model attributes in certain cases, and the JSPs/tags then check whether they are set, so accesses to non-existing attributes happen very frequently, which leads to noticeable performance problems.
Root cause seems to be that a different
ExpressionFactory
is used (com.sun.el.ExpressionFactoryImpl
in 2.3.0+ instead oforg.apache.el.ExpressionFactoryImpl
in 2.2.x). The latter sets a certain context attribute to true (see below), but the former does not.The
ScopedAttributeELResolver
contains this code:AST_IDENTIFIER_KEY is the class
org.apache.el.parser.AstIdentifier
.In Spring Boot 2.2.x,
resolveClass
is set tofalse
since the context boolean istrue
, in 2.3.0 and beyond it stays attrue
. The performance problem occurs whenimportHandler.resolveClass(key)
is called, which does the four class lookups.The class that should normally set this attribute is
org.apache.el.parser.AstIdentifier
(which is not used in Spring Boot 2.3.0+, therecom.sun.el.parser.AstIdentifier
is used), using this code:The class loading becomes visible when setting
logging.level.org.apache.catalina.loader=DEBUG
In Spring Boot 2.3.0 and beyond, the following is logged for each request when accessing
${myProperty}
:This is easily reproducible in a minimalistic Spring Boot application. Locally, with classloader logging disabled, I get around 4000 requests per second when hammering a simple JSP that accesses 30 different non-existing model attributes in Spring Boot 2.2.12. In all later Spring Boot versions, I get around 33 requests per second only.
You can find the test application here: http://www.schuerger.com/spring-boot/test-app.tar.gz
Run it with
Then use
ApacheBench
to URL-hammer it:Afterwards, edit pom.xml to switch to version 2.2.12.RELEASE, repeat and compare. Disable the class loading logging for more realistic results.
Compare this to when changing the
TestController
to set all of the 30 attributes to a dummy value instead of leaving them undefined. Afterwards, the performance between the Spring Boot versions is similar.Note that this is most likely not a Tomcat issue. I have tested this locally with different Tomcat versions (9.0.34, 9.0.35 and 9.0.41) with different combinations of Spring Boot. Note that Spring Boot 2.2.7 by default uses version 9.0.34, 2.2.12 uses 9.0.41 and 2.3.0 uses 9.0.35. The problem occurs in 2.3.x and 2.4.x regardless of the Tomcat version used and does not occur in 2.2.x regardless of the Tomcat version used.
The text was updated successfully, but these errors were encountered: