caffeine: WrongMethodTypeException after upgrade to 3.1.7
I don’t have a reproducer for this yet, but I’ve seen it pop up in a variety of contexts when building a caffeine cache. This has occurred on a few configurations using both build
and buildAsync
. #905 seems like the most likely culprit.
java.lang.invoke.WrongMethodTypeException: expected ()NodeFactory but found ()NodeFactory
at java.base/java.lang.invoke.Invokers.newWrongMethodTypeException(Invokers.java:523)
at java.base/java.lang.invoke.Invokers.checkExactType(Invokers.java:532)
at com.github.benmanes.caffeine.cache.NodeFactory.newFactory(NodeFactory.java:152)
at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1708)
at com.github.benmanes.caffeine.cache.NodeFactory.loadFactory(NodeFactory.java:141)
at com.github.benmanes.caffeine.cache.NodeFactory.newFactory(NodeFactory.java:89)
at com.github.benmanes.caffeine.cache.BoundedLocalCache.<init>(BoundedLocalCache.java:269)
at com.github.benmanes.caffeine.cache.SS.<init>(Unknown Source)
at com.github.benmanes.caffeine.cache.SSL.<init>(Unknown Source)
at com.github.benmanes.caffeine.cache.SSLMW.<init>(Unknown Source)
at com.github.benmanes.caffeine.cache.SSLMWA.<init>(Unknown Source)
at com.github.benmanes.caffeine.cache.LocalCacheFactory.newBoundedLocalCache(LocalCacheFactory.java:47)
at com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalManualCache.<init>(BoundedLocalCache.java:3952)
at com.github.benmanes.caffeine.cache.BoundedLocalCache$BoundedLocalManualCache.<init>(BoundedLocalCache.java:3948)
at com.github.benmanes.caffeine.cache.Caffeine.build(Caffeine.java:1048)
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 30 (25 by maintainers)
Commits related to this issue
- Fix issue #1111 by not creating methodType on each invocation (to work around a possible concurrency bug in JDK) — committed to uschindler/caffeine by uschindler a year ago
- Fix issue #1111 by not creating methodType on each invocation (to work around a possible concurrency bug in JDK) (#1114) — committed to ben-manes/caffeine by uschindler a year ago
Thank you both!
Here you are: #1114
Will do. For the other ones I will use a static final method type for safety.
To me this looks like a Hotspot bug…
I haven’t seen this anywhere. In addition the error message makes no sense. The signature matches exactly: expected ()NodeFactory but found ()NodeFactory
I have not changed the benchmark, but I think you know wthat I did - maybe adapt the benchmark classes, too. Actually the LocalCacheFactory is the way how one should normally use MethodType instances: Do not create them over an over and keep them as static finals. If you look at the code inside the JDK how they use it for bootstrapping.
I am quite sure that there is some strange problem inside the JDK that sometimes creates non-interned MethodTypes under high load.
I wonder if it is expunging a temporary
WeakEntry
when it fails the race, which causes it to remove a live entry and create duplicates. Following the jdk sources.When it optimistically creates a
WeakEntry<>(elem, stale)
, that instance is registered on the reference queue for tracking. When a race causes another entry to be cached, then the loser’s is discarded. That makes both the entry and itselem
eligible for collection. One might expectelem
to live longer than the entry since it was a method parameter, but since its unused that might be elided by escape analysis and inlining. So possibly some reordering and lifetimes might cause the GC to think that theWeakReference
is still alive and should be enqueued. Since themap.remove(e)
is based on object equality, it could discard a different instance than the collected one as we no longer can assume a strict state ordering. A simple fix would be to usemap.computeIfPresent(reference, (k, v) -> (reference == v) ? null : v)
.I am kind of reaching, but it seems just plausible enough to happen, not be obvious to the author, and pretty hard to orchestrate that race. does this seem believable?