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

Most upvoted comments

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 its elem eligible for collection. One might expect elem 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 the WeakReference is still alive and should be enqueued. Since the map.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 use map.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?