Skip to content

Conversation

@chschu
Copy link

@chschu chschu commented Feb 10, 2026

After updating from Spring Framework v6.2.11 to v6.2.15, we observed heavy lock congestion even under moderate load. The following stacktrace shows the culprit:

"https-jsse-nio-31030-exec-13" - Thread t@166
   java.lang.Thread.State: WAITING
	at [email protected]/jdk.internal.misc.Unsafe.park(Native Method)
	- parking to wait for <3441435a> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
	at [email protected]/java.util.concurrent.locks.LockSupport.park(LockSupport.java:223)
	at [email protected]/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:790)
	at [email protected]/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1030)
	at [email protected]/java.util.concurrent.locks.ReentrantLock$Sync.lock(ReentrantLock.java:154)
	at [email protected]/java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:323)
	at org.springframework.util.ConcurrentReferenceHashMap$Segment.doTask(ConcurrentReferenceHashMap.java:675)
	at org.springframework.util.ConcurrentReferenceHashMap.doTask(ConcurrentReferenceHashMap.java:570)
	at org.springframework.util.ConcurrentReferenceHashMap.computeIfAbsent(ConcurrentReferenceHashMap.java:389)
	at org.springframework.core.annotation.AnnotationTypeMappings.forAnnotationType(AnnotationTypeMappings.java:230)
	at org.springframework.core.annotation.AnnotationTypeMappings.forAnnotationType(AnnotationTypeMappings.java:206)
	at org.springframework.core.annotation.TypeMappedAnnotations$MergedAnnotationFinder.process(TypeMappedAnnotations.java:426)
	at org.springframework.core.annotation.TypeMappedAnnotations$MergedAnnotationFinder.doWithAnnotations(TypeMappedAnnotations.java:406)
	at org.springframework.core.annotation.TypeMappedAnnotations$MergedAnnotationFinder.doWithAnnotations(TypeMappedAnnotations.java:372)
	at org.springframework.core.annotation.AnnotationsScanner.processMethodAnnotations(AnnotationsScanner.java:395)
	at org.springframework.core.annotation.AnnotationsScanner.processMethodHierarchy(AnnotationsScanner.java:282)
	at org.springframework.core.annotation.AnnotationsScanner.processMethod(AnnotationsScanner.java:247)
	at org.springframework.core.annotation.AnnotationsScanner.process(AnnotationsScanner.java:95)
	at org.springframework.core.annotation.AnnotationsScanner.scan(AnnotationsScanner.java:82)
	at org.springframework.core.annotation.TypeMappedAnnotations.scan(TypeMappedAnnotations.java:248)
	at org.springframework.core.annotation.TypeMappedAnnotations.get(TypeMappedAnnotations.java:155)
	at org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation(AnnotatedElementUtils.java:648)
	at org.springframework.core.annotation.AnnotatedMethod.getMethodAnnotation(AnnotatedMethod.java:155)
	at org.springframework.core.annotation.AnnotatedMethod$AnnotatedMethodParameter.getMethodAnnotation(AnnotatedMethod.java:286)
	at org.springframework.web.method.annotation.ModelFactory.getNameForReturnValue(ModelFactory.java:271)
	at org.springframework.web.method.annotation.ModelFactory.invokeModelAttributeMethods(ModelFactory.java:153)
	at org.springframework.web.method.annotation.ModelFactory.initModel(ModelFactory.java:111)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:975)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:896)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903)
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564)
	<redacted>

Most of the time, the call to ConcurrentReferenceHashMap.computeIfAbsent(...) is read-only and doesn't need the lock.

The implementations of computeIfAbsent(...) and computeIfPresent(...) have been added in v7.0.0 with commit 0552cdb and backported to v6.2.13 with commit 12dd758.

Please note that this issue is different from the one described in gh-35944, because it doesn't involve a call to AnnotationTypeMappings$Cache.get(...).

This PR adds lock-free checks to these methods to avoid locking in the predominant read-only case. It has been verified to solve the lock congestion issue in our load tests.

The modification drastically improves the performace of the read-only case, but adds some overhead (an entry lookup) in the modifying case. This tradeoff might not be feasible for all usages of ConcurrentReferenceHashMap. A similar issue has been identified (and improved, considering the inherent tradeoff) almost a decade ago in the JDK: https://bugs.openjdk.java.net/browse/JDK-8161372

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Feb 10, 2026
…ion of computeIfAbsent and computeIfPresent

Signed-off-by: Christian Schuster <[email protected]>
@chschu chschu force-pushed the bugfix/annotation-lookup-lock-congestion branch from 16ab7e0 to cbac801 Compare February 10, 2026 16:32
@jhoeller jhoeller self-assigned this Feb 11, 2026
@jhoeller jhoeller added type: regression A bug that is also a regression in: core Issues in core modules (aop, beans, core, context, expression) and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Feb 11, 2026
@jhoeller jhoeller added this to the 7.0.4 milestone Feb 11, 2026
@jhoeller jhoeller added the for: backport-to-6.2.x Marks an issue as a candidate for backport to 6.2.x label Feb 11, 2026
@jhoeller jhoeller added status: backported An issue that has been backported to maintenance branches and removed for: backport-to-6.2.x Marks an issue as a candidate for backport to 6.2.x labels Feb 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in: core Issues in core modules (aop, beans, core, context, expression) status: backported An issue that has been backported to maintenance branches type: regression A bug that is also a regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants