-
Notifications
You must be signed in to change notification settings - Fork 38.5k
Major performance slowdown in AnnotationUtils.findAnnotation [SPR-7630] #12286
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
Comments
Juergen Hoeller commented Of those 1.3 million calls, how often is this being invoked for the very same method signatures and target interfaces? I suppose you got many objects which implement the same interfaces but never actually contain annotations on any interface method? Are only the interfaces the same, or the searched classes as well? Juergen |
Stéphane Nicoll commented Spring batch listener registration (spring 3) |
Stéphane Nicoll commented Spring batch listener registration (spring 2.5) |
Stéphane Nicoll commented arguments of getMethodInvokerByAnnotation with Spring 3 |
Stéphane Nicoll commented I attached everything I found relevant for you. If you look at the call stack, most of the getMethod calls are from the Spring batch listeners registration process (registerStepListeners and registerItemListeners respectively). Eventually, both end up in I have attached every call to this method in the attached excel file (ugh!). The first column is the class name of the object (or proxy around it) and the second column is the required annotation type. You'll see by yourself but it seems it is called four times for the same annotation and the same target (actually 2 times for the step listener registration and 2 times for the item listener registration). I don't know if we can avoid this duplication (Dave might definitely have more insight) but the difference between 2.5 and 3.0 is obvious and is located in Spring core. I have attached the call stack of the same registration process with Spring 2.5 for you to compare. Is this enough? Thanks! |
Stéphane Nicoll commented And to answer your question, for one part of the registration we have 2224 calls (you will find the details in the excel sheet) leading to 67488 invocations of the AnnotationUtils.findAnnotation. These 67488 invocations lead to half a million getMethod calls roughly. We might be able to cache this information in Spring batch and reduce the number of calls from 2224 to 556. I have no idea if things could be cached as well in the core framework itself. |
Juergen Hoeller commented Thanks for the additional context, Stephane - that definitely brings us one step further. The change in Spring 3.0 is that we're searching for annotations on implemented interfaces as well, i.e. on methods with the same signature as the one we're given, declared on any implemented interface. This involves getMethod calls for the signature check, and this seems to be making the difference in your case. I wonder how many interfaces your objects implement as they are being given to AnnotationUtils.findAnnotation... Probably always the same Spring batch callback interfaces? Spring 3.0 would search those interfaces for annotations again and again, which of course is in vain since it'll never find an annotation on those. Juergen |
Stéphane Nicoll commented Juergen, I ran another profiling session to get the details and here's some output (it's not fully complete but you have most of the objects being involved in the initial excel sheet.
From what I can see we search for annotations on the proxy, which seems weird to me (ScopedObject, AopInfrastructureBean, Advised, SpringProxy). It has 25 methods (23 only in Advised!). I also think that this change in Spring 3 is probably against what is done in Spring Batch since these annotations are not meant (IMHO) to be put on an interface: they are primarily used as callback tags for listener events. |
Dave Syer commented Maybe Spring could do some caching? In this case it would help to aggressively cache negative results: if a method has no annotations it can be ignored on all subsequent calls. |
Juergen Hoeller commented I've finally addressed this through caching of interfaces in AnnotationUtils: We're storing whether a given interface has any methods with annotations in a static WeakHashMap. This shouldn't cause issues with garbage collection since the values are plain booleans, so there are no references back to the keys. I hope this actually provides the speed-up that you're expecting. It's the best we can do without changing semantics. This will be available in tomorrow morning's 3.0.5 snapshot. Juergen |
Stéphane Nicoll commented Great! This fixes indeed the underlying issue. Some facts about my simple test that bootstrap the container:
Also attached relevant back trace that shows the problem is fixed for my use case. Thanks Juergen! |
Stéphane Nicoll opened SPR-7630 and commented
While migrating our app from 2.5.7 to 3.0.4 we have noticed a major slowdown in the application context's startup.
Our application is using Spring Batch and has 22 batches. I have ran a profiling session and noticed an abusive call to
java.lang.Class.getMethod(String, Class[])
.The culprit seems to be a change in
AnnotationUtils.findAnnotation(Method,Class)
.Attached the back trace calls for both Spring 2.5 and 3.0.4. The only thing that changed between the profiling sessions is the Spring version and the profiler only starts the (same) application context.
I also have the yourkit snapshots if you are interested to run a snapshot comparison.
This is obviously blocking us to upgrade. Let me know if there anything I can do to help.
Affects: 3.0.4
Reference URL: http://forum.springsource.org/showthread.php?p=323388#post323388
Attachments:
Issue Links:
Referenced from: commits 3c067e5
1 votes, 5 watchers
The text was updated successfully, but these errors were encountered: