JDK-8201248 : [AOT] AssertionError: duplicate classes for name Ljava/io/PrintStream
  • Type: Bug
  • Component: hotspot
  • Sub-Component: compiler
  • Affected Version: 10,11,12
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2018-04-06
  • Updated: 2023-07-21
  • Resolved: 2023-07-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 12
12Resolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
Test: compiler/aot/cli/MultipleAOTLibraryTest.java

Exception in thread "main" java.lang.AssertionError: duplicate classes for name Ljava/io/PrintStream;
	at jdk.aot/jdk.tools.jaotc.AOTCompiledClass.getAOTKlassData(AOTCompiledClass.java:341)
	at jdk.aot/jdk.tools.jaotc.AOTCompiledClass.addFingerprintKlassData(AOTCompiledClass.java:371)
	at jdk.aot/jdk.tools.jaotc.CompiledMethodInfo.addDependentKlassData(CompiledMethodInfo.java:293)
	at jdk.aot/jdk.tools.jaotc.InfopointProcessor.recordScopeKlasses(InfopointProcessor.java:89)
	at jdk.aot/jdk.tools.jaotc.InfopointProcessor.recordScopeKlasses(InfopointProcessor.java:84)
	at jdk.aot/jdk.tools.jaotc.InfopointProcessor.process(InfopointProcessor.java:78)
	at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processInfopointsAndMarks(MetadataBuilder.java:222)
	at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processMetadataClass(MetadataBuilder.java:69)
	at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processMetadata(MetadataBuilder.java:63)
	at jdk.aot/jdk.tools.jaotc.DataBuilder.prepareData(DataBuilder.java:161)
	at jdk.aot/jdk.tools.jaotc.Main.run(Main.java:188)
	at jdk.aot/jdk.tools.jaotc.Main.run(Main.java:101)
	at jdk.aot/jdk.tools.jaotc.Main.main(Main.java:80)

Comments
That makes sense. I wonder if there are other places in Graal that would need to be fixed. And there should probably be a warning in the API descriptions saying to use equals().
08-12-2018

Regardless of the cause, I think we still should be using equals(). There is nothing in the API that says it's a singleton.
08-12-2018

ClassValue uses a WeakHashMap, so we might not even need to use WeakReferences at all. I think it depends on what kind of leak we are trying to prevent.
08-12-2018

Agreed, that race is possible, but it also feels like it's not the only one.
08-12-2018

I think ClassValue is fine, we're just using it wrong. I think the race in fromClass is the problem, even though it might look unlikely. If more than one compiler thread is calling fromClass for the same class at the same time, and this is the initial lookup, then there are no existing ResolvedJavaType strong references to keep the WeakReference alive: protected WeakReference<ResolvedJavaType> computeValue(Class<?> type) { return new WeakReference<>(createClass(type)); } I think all it takes is for 2 threads to get (javaType == null) at the same time, then the last thread to call remove() will erase the unique value given to the other thread, resulting in a 2nd unique value. We can fix the race using synchronization, but if that would cause a bottleneck then we need something like a pinnable weak-ref (there's one in sun/rmi/transport/WeakRef.java).
08-12-2018

I think something is either wrong with ClassValue or I don't quite understand it's behavior. The duplicate references are not really duplicates, they definitely refer to exactly the same class. So, the solution would be to simply use equals() instead of ==, but it would be nice to understand whether ClassValue is working correctly.
07-12-2018

I suspect this could be because of the myriad of problems with classloaders. Let's wait for the next update and see if it makes this go away.
01-11-2018

Everything is the same: java.lang.AssertionError: duplicate classes for name Lcompiler/calls/common/CallsBase;, fingerprints old: 27927869127835, new: 27927869127835, klass pointers old: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>}, new: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>} at jdk.aot@12-internal/jdk.tools.jaotc.AOTCompiledClass.getAOTKlassData(AOTCompiledClass.java:337)
12-09-2018

Yes, it is the same. We added additional output to find the cause. Please, add your log files to this bug.
12-09-2018

Can this problem also look like: Exception: java.lang.AssertionError: duplicate classes for name Lcompiler/calls/common/CallsBase;, fingerprints old: ..., new: ..., klass pointers old: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>}, new: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>} Exception: stdout: [Exception in thread "main" java.lang.AssertionError: duplicate classes for name Lcompiler/calls/common/CallsBase;, fingerprints old: ..., new: ..., klass pointers old: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>}, new: meta{HotSpotType<Lcompiler/calls/common/CallsBase;, resolved>} I'm testing a change that shouldn't have anything to do with this.
12-09-2018

I guess it is possible, but it requires not only two threads making the same call, but also a GC happening at the right moment to clear the reference. What is mind boggling about this case that it's almost always PrintStream (we seen other classes, but this one is prevalent by far).
07-09-2018

"Should HotSpotJVMCIMetaAccessContext.fromClass be synchronized?" Consider 3 threads calling fromClass(). The first two threads take the remove path the first time through the loop. The third thread returns a value. The order of events could look like this: T1: type = resolvedJavaType.get() T1: javaType = type.get(); javaType == null T2: type = resolvedJavaType.get() T2: javaType = type.get(); javaType == null T2: resolvedJavaType.remove() T3: type = resolvedJavaType.get() T3: javaType = type.get(); javaType != null T3: return javaType [1] T1: resolvedJavaType.remove() T1: loop again T1: type = resolvedJavaType.get() T1: javaType = type.get(); javaType != null T1: return javaType [2] T2: loop again T2: type = resolvedJavaType.get() T2: javaType = type.get(); javaType != null T2: return javaType [2] Won't values [1] and [2] be different because they have a call to remove() in between?
07-09-2018

The trouble is that we actually hold a strong reference to HotSpotResolvedObjectType in AOTCompiledClass. I don't see how it can go away? Unless the machinery that keeps alive metadata doesn't work and we end up with stale metadata pointers. But we'd probably crash horribly is that were the case.
07-09-2018

It looks like ClassValue is already using WeakReference's for values. I wonder if anything could go wrong because Graal is also wrapping values in WeakReference.
07-09-2018

The pointers are pretty well hidden. What we actually can do is call equals(). That should call the implementation in HotSpotResolvedJavaType.equals() that would compare mirrors. I think that'd be good enough.
07-09-2018

Hm.. It didn't print the pointers.
07-09-2018

New diagnostics shows that the fingerprints match. Can we get access to the actual metadata pointer?
07-09-2018

I agree with more diagnostic for now.
17-07-2018

So, I was unable to reproduce it no matter how I try. I'm going to push a change to print more diagnostics should this assert fire again.
17-07-2018

There is also an instance when it failed for Lcompiler/calls/common/CallsBase;
10-07-2018

If the race is always on the same class, java/io/PrintStream, that's a little suspicious on its own. Also, I don't see how that class could ever get unloaded, since it's loaded by the system classloader. And I don't think jaotc uses different classloaders. If wonder if we are using the wrong name somewhere, something like L<name> instead of [L<name>.
10-07-2018

I don't think so. The underlying storage in ClassValue should take care of everything.
09-07-2018

Should HotSpotJVMCIMetaAccessContext.fromClass be synchronized?
09-07-2018

So, somehow we're ending up with multiple instances of HotSpotResolvedJavaType that has the same name. I don't think this should happen unless the same class had been loaded by multiple class loaders and the result of the compilation uses both. What is truly bizarre is the racy nature of the problem.
05-07-2018

ILW=AOT test failure with duplicate classes, tier1; intermittent ; none = HLH = P2
11-06-2018

Same assertion for in other test compiler/aot/RecompilationTest.java ----------System.err:(33/2078)---------- stdout: [CompileCommand: dontinline compiler/whitebox/SimpleTestCaseHelper.* Exception in thread "main" java.lang.AssertionError: duplicate classes for name Ljava/lang/Integer; at jdk.aot/jdk.tools.jaotc.AOTCompiledClass.getAOTKlassData(AOTCompiledClass.java:341) at jdk.aot/jdk.tools.jaotc.AOTCompiledClass.addFingerprintKlassData(AOTCompiledClass.java:371) at jdk.aot/jdk.tools.jaotc.CompiledMethodInfo.addDependentKlassData(CompiledMethodInfo.java:295) at jdk.aot/jdk.tools.jaotc.InfopointProcessor.recordScopeKlasses(InfopointProcessor.java:96) at jdk.aot/jdk.tools.jaotc.InfopointProcessor.recordScopeKlasses(InfopointProcessor.java:84) at jdk.aot/jdk.tools.jaotc.InfopointProcessor.process(InfopointProcessor.java:78) at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processInfopointsAndMarks(MetadataBuilder.java:222) at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processMetadataClass(MetadataBuilder.java:69) at jdk.aot/jdk.tools.jaotc.MetadataBuilder.processMetadata(MetadataBuilder.java:63) at jdk.aot/jdk.tools.jaotc.DataBuilder.prepareData(DataBuilder.java:161) at jdk.aot/jdk.tools.jaotc.Main.run(Main.java:188) at jdk.aot/jdk.tools.jaotc.Main.run(Main.java:101) at jdk.aot/jdk.tools.jaotc.Main.main(Main.java:80) ]; stderr: [] exitValue = 1
11-06-2018

Same failure with AotInvokeInterface2AotTest.java on Linux x64 (JDK-8203835).
28-05-2018

I tried downloading the test artifacts and running it locally, but it did not fail.
09-05-2018

So far this has only failed on Windows and Macos. Both have case-insensitive filesystems. Could another test be leaving a java/io/printstream.class lying around for AOT to find on the classpath?
09-05-2018

I can't reproduce it either. Bug history still shows only 1 failure.
03-05-2018

[~dlong] This is new code added with JDK-8132547 changes. Dean, please, look.
20-04-2018

Also not able to reproduce reported issue with compiler/aot/cli/MultipleAOTLibraryTest.java test with multiple runs in my local.
13-04-2018

This failure has only occurred once. The test is executed successfully several times every nightly. I'll tag it as intermittent and remove the integration_blocker label.
13-04-2018

initial ILW = AOT test failure with duplicate classes AssertionError; with jaotc; none = HLH = P2
11-04-2018