-
Bug
-
Resolution: Unresolved
-
P4
-
None
-
21, 25
See the original report here:
https://mail.openjdk.org/pipermail/shenandoah-dev/2025-February/025477.html
Reproduces well with:
$ build/linux-x86_64-server-fastdebug/images/jdk/bin/java -XX:+UseShenandoahGC -XX:StartFlightRecording=filename=100us.jfr -XX:+ShenandoahVerify -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGuaranteedGCInterval=1000 -Xlog:gc ClassUnloadTest.java
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (/home/shade/trunks/jdk/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp:110), pid=1616897, tid=1616904
# Error: Before Updating References, Marked; Must be marked in complete bitmap
Referenced from:
interior location: 0x00000007fef6434c
0x00000007fef64318 - klass 0x000073546e1baeb8 java.lang.Class
allocated after mark start
not after update watermark
marked strong
marked weak
not in collection set
mark: mark(is_unlocked hash=0x000000002a90b490 age=0)
region: | 3833|R |Y|BTE 7fe800000, 7fef92328, 7ff000000|TAMS 7fef547a8|UWM 7fef92328|U 7752K|T 0B|G 7344K|S 407K|L 5919K|CP 0
class: DynamicClass
classloader: 0x000000079001cd00
classloader after TAMS: false
classloader marked: false
Object:
0x000000079001ce88 - klass 0x000073546e1c23c0 java.lang.Module
not allocated after mark start
not after update watermark
not marked strong
not marked weak
in collection set
mark: mark(is_unlocked no_hash age=0)
region: | 3612|CS |Y|BTE 790000000, 790800000, 790800000|TAMS 790800000|UWM 790800000|U 8192K|T 8192K|G 0B|S 0B|L 20936B|CP 0
module: unnamed module
Forwardee:
(the object itself)
I have explored this a bit. It looks to me the DynamicClass gets its "module" from the classloader "unnamed module" oop. Looks like DynamicClass gets allocated after TAMS, and thus is treated as implicitly alive. For some reason the class/instance is treated as dead. (Hypothesis: the class is not on stack anymore, since we have left the method, but the object is already on heap.) Since the class is not reachable anymore, it somehow gets unloaded, and its classloader is marked dead. Since classloader is dead, we do not mark/evac its "unnamed module" oop either. So now we are in the situation where a "marked" Class object references the unmarked oop.
Still not clear how JFR participates in this, but it might be related to JDK-8340364.
Some debugging breadcrumbs here:
https://github.com/openjdk/jdk/compare/master...shipilev:jdk:JDK-8350580-shenandoah-dynamic-class
https://mail.openjdk.org/pipermail/shenandoah-dev/2025-February/025477.html
Reproduces well with:
$ build/linux-x86_64-server-fastdebug/images/jdk/bin/java -XX:+UseShenandoahGC -XX:StartFlightRecording=filename=100us.jfr -XX:+ShenandoahVerify -XX:+UnlockExperimentalVMOptions -XX:ShenandoahGuaranteedGCInterval=1000 -Xlog:gc ClassUnloadTest.java
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (/home/shade/trunks/jdk/src/hotspot/share/gc/shenandoah/shenandoahVerifier.cpp:110), pid=1616897, tid=1616904
# Error: Before Updating References, Marked; Must be marked in complete bitmap
Referenced from:
interior location: 0x00000007fef6434c
0x00000007fef64318 - klass 0x000073546e1baeb8 java.lang.Class
allocated after mark start
not after update watermark
marked strong
marked weak
not in collection set
mark: mark(is_unlocked hash=0x000000002a90b490 age=0)
region: | 3833|R |Y|BTE 7fe800000, 7fef92328, 7ff000000|TAMS 7fef547a8|UWM 7fef92328|U 7752K|T 0B|G 7344K|S 407K|L 5919K|CP 0
class: DynamicClass
classloader: 0x000000079001cd00
classloader after TAMS: false
classloader marked: false
Object:
0x000000079001ce88 - klass 0x000073546e1c23c0 java.lang.Module
not allocated after mark start
not after update watermark
not marked strong
not marked weak
in collection set
mark: mark(is_unlocked no_hash age=0)
region: | 3612|CS |Y|BTE 790000000, 790800000, 790800000|TAMS 790800000|UWM 790800000|U 8192K|T 8192K|G 0B|S 0B|L 20936B|CP 0
module: unnamed module
Forwardee:
(the object itself)
I have explored this a bit. It looks to me the DynamicClass gets its "module" from the classloader "unnamed module" oop. Looks like DynamicClass gets allocated after TAMS, and thus is treated as implicitly alive. For some reason the class/instance is treated as dead. (Hypothesis: the class is not on stack anymore, since we have left the method, but the object is already on heap.) Since the class is not reachable anymore, it somehow gets unloaded, and its classloader is marked dead. Since classloader is dead, we do not mark/evac its "unnamed module" oop either. So now we are in the situation where a "marked" Class object references the unmarked oop.
Still not clear how JFR participates in this, but it might be related to JDK-8340364.
Some debugging breadcrumbs here:
https://github.com/openjdk/jdk/compare/master...shipilev:jdk:JDK-8350580-shenandoah-dynamic-class
- relates to
-
JDK-8340364 Shenandoah: Dead class mirrors crash GC
-
- Open
-