Issue 1:
Kevin Mudrick opened SPR-9670 and commented

We are seeing a pretty substantial performance hit with the creation of large quantities of prototype-scoped beans in the 3.1.x branch versus 3.0.x (and 2.5.6)

In our example, we have call context.getBean() with a bean consisting of 2 levels of nested bean referenced, in a pretty large loop.

3.0.7: 40604ms

3.1.2: 685150ms

The only difference between these runs is the version of the spring dependencies.

After profiling using YourKit, I found the hotspots in 3.1.x to be in the following places:

org.springframework.beans.TypeConverterDelegate: This seems to be the bigger problem.

The changes introduced in 3.1 (specifically, in commit 6f14673) to findDefaultEditor() end up (expensively) initializing an editor registry every time this is called. Since we get here each time we apply a property on every bean creation, this really adds up. Previously, the commented-out code ended up shortcutting this. I found that replacing the commented out code (the part that relied on the now-removed PropertyTypeDescriptor/PropertyDescriptor with static use of PropertyEditorManager.findEditor(), cuts down on the slowness considerably, at the expense of relying on java.beans - which a few commit comments seem to indicate is something to be avoided for better platform compatibility.

org.springframework.core.convert.Property: This also seems to be a problem.

The overhead of the annotation parsing via reflection is already noted in #13981 - but that issue doesn't seem to mention this example. My profiling is showing that this is being constructed for every property found on every bean being created. With lots of prototype beans - this comes to light pretty quickly. Perhaps if ReflectionUtils used a similar caching strategy to apache commons-beanutils PropertyUtilsBean for annotations, this performance hit could be mitigated.

Affects: 3.1.2

Issue Links:

#13981 org.springframework.core.convert.Property class performs expensive operations in its constructor ("is duplicated by")

#12081 Concurrent prototype creation causes NullPointerException

#13109 TypeConverterDelegate contains commented out code

0 votes, 6 watchers

Issue 2:
Daniel Gredler opened SPR-9601 and commented

As a result of the changes [1] made to RequestMappingHandlerMapping.isHandler(Class) to make it detect both Controller and RequestMapping annotations (see #14010), one of our HttpRequestHandler implementations that was working in 3.1.1 no longer works in 3.1.2:

@RequestMapping("/console")
@Transactional
public class ConsoleHandler implements HttpRequestHandler, ApplicationContextAware {

...

@RequestMapping(method = RequestMethod.GET)
public void handleRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
...
}

...
}

In 3.1.1, this handler was mapped by the DefaultAnnotationHandlerMapping, which was able to forward HTTP requests to this handler. However, because of the above change, in 3.1.2 this handler is mapped by the RequestMappingHandlerMapping, which throws the following exception when it tries to actually handle an HTTP request:

java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details:
Controller [$Proxy109]
Method [public void com.dpgm.connect.framework.server.remoting.ConsoleHandler.handleRequest(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException,java.io.IOException]
Resolved arguments:
[0] [type=org.eclipse.jetty.server.Request] [value=(GET /misc/console)@10144214 org.eclipse.jetty.server.Request@9ac9d6]
[1] [type=org.eclipse.jetty.server.Response] [value=HTTP/1.1 200
]
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:217)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:693)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:806)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:558)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:488)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:119)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:520)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:233)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:973)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:417)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:907)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:47)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
at org.eclipse.jetty.server.Server.handle(Server.java:350)
at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:442)
at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:924)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:582)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
... 34 more

I'm attaching the debug logs of the server startup + one HTTP request, one log file using Spring 3.1.1 and one log file using Spring 3.1.2. I've massaged the logs a little bit (removed most timestamps, normalized the hashcodes in the toStrings) so that it's easier to see the differences if you diff the files.

[1] 2fa0e63

Affects: 3.1.2

Attachments:

console-out-3.1.1.txt (675.28 kB)

console-out-3.1.2.txt (687.57 kB)

Referenced from: commits 7c6a1a1