-
Type:
Bug
-
Resolution: Not an Issue
-
Priority:
P4
-
None
-
Affects Version/s: 27
-
Component/s: hotspot
-
generic
-
generic
ADDITIONAL SYSTEM INFORMATION :
- Operating system: Linux
- Compiler/toolchain: GCC 9.5
- Source commit: a8784a04081a11a41fe0e3bd8ab9fba6af2d3d4d (tag: jdk-17.0.18+4, branch: master)
- Build type: fastdebug
- Coverage: enabled (built with --enable-coverage)
- Build environment: Linux, compiled from source with GCC 9.5
A DESCRIPTION OF THE PROBLEM :
- I observe a deterministic VM crash (fastdebug build) when running a slightly modified version of test/hotspot/jtreg/compiler/codecache/jmx/PeakUsageTest.java.
- The test allocates a code blob using WhiteBox.allocateCodeBlob and later, in a finally block, calls WhiteBox.freeCodeBlob on the same address inside a loop (freeing the same blob many times).
- Expected output: “PASS”. Actual behavior: VM aborts with an internal assertion failure in nmethod::flush:
- assert(is_zombie() || is_osr_method()) failed: must be a zombie method
- Location: src/hotspot/share/code/nmethod.cpp:1548
- Problematic frame: V [libjvm.so+0x27ba414] nmethod::flush()+0x3e4
- The crash reproduces consistently with the flags below. This suggests that repeatedly invoking WhiteBox.freeCodeBlob on an already-freed/invalid blob can drive the nmethod lifecycle into an unexpected state where nmethod::flush() runs on a non-zombie, triggering the assert. Even if the test misuses WhiteBox (double-free), the VM abort seems undesirable for WhiteBox-based testing; a safer behavior might be to make freeCodeBlob idempotent or return an error without tripping asserts.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Same as the way to run `test/hotspot/jtreg/compiler/codecache/jmx/PeakUsageTest.java`.
ACTUAL -
JRE version: OpenJDK Runtime Environment (17.0.18) (build 17.0.18-internal+0-adhoc.syc.jdk17u)
Java VM: OpenJDK 64-Bit Server VM (17.0.18-internal+0-adhoc.syc.jdk17u, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
Problematic frame:
V [libjvm.so+0x5692ef] ciObjectFactory::create_new_metadata(Metadata*)+0x2f
---------- BEGIN SOURCE ----------
package compiler.codecache.jmx;
import jdk.test.whitebox.code.BlobType;
import java.lang.management.MemoryPoolMXBean;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class PeakUsageTest {
private final BlobType btype;
public PeakUsageTest(BlobType btype) {
this.btype = btype;
}
public static void main(java.lang.String[] args) {
for (BlobType btype : BlobType.getAvailable()) {
new PeakUsageTest(btype).runTest();
}
}
protected void runTest() {
MemoryPoolMXBean bean = btype.getMemoryPool();
bean.resetPeakUsage();
long addr = CodeCacheUtils.WB.allocateCodeBlob(CodeCacheUtils.ALLOCATION_SIZE, btype.id);
int lvInt3 = -803;
int lvInt1 = -803;
int lvInt2 = -803;
boolean lvBool1 = false;
String lvStr1 = new String("asdfasf123123");
AtomicReferenceArray<String> lvAtom1 = new AtomicReferenceArray<String>(64);
for (int tmpi = 0; tmpi < 64; tmpi++) {
lvAtom1.set(tmpi, "str" + tmpi);
}
try {
long currUsage = bean.getUsage().getUsed();
long peakUsage = bean.getPeakUsage().getUsed();
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
"Peak usage does not match usage after allocation for " + bean.getName());
} finally {
if (addr != 0) {
CodeCacheUtils.WB.freeCodeBlob(addr);
}
}
bean.resetPeakUsage();
long currUsage = bean.getUsage().getUsed();
long peakUsage = bean.getPeakUsage().getUsed();
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
"Code cache peak usage is not equal to usage after reset for " + bean.getName());
long addr2 = CodeCacheUtils.WB.allocateCodeBlob(CodeCacheUtils.ALLOCATION_SIZE, btype.id);
try {
for (lvInt1 = 0; lvInt1 < 333; lvInt1++) {
currUsage = bean.getUsage().getUsed();
peakUsage = bean.getPeakUsage().getUsed();
for (lvInt2 = 0; lvInt2 < 333; lvInt2++) {
int tmpVar1 = lvInt1 + lvInt3;
int tmpVar2 = (lvInt1 * lvInt3) - lvInt1;
lvInt1 = tmpVar1 + tmpVar2;
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
("Code cache peak usage is not equal to usage after fresh "
+ "allocation for ")
+ bean.getName());
}
}
} finally {
if (addr2 != 0) {
for (lvInt1 = 0; lvInt1 < 333; lvInt1++) {
CodeCacheUtils.WB.freeCodeBlob(addr2);
lvBool1 = lvAtom1.compareAndSet(32, lvStr1, lvStr1);
}
}
}
System.out.println("PASS");
}
}
---------- END SOURCE ----------
- Operating system: Linux
- Compiler/toolchain: GCC 9.5
- Source commit: a8784a04081a11a41fe0e3bd8ab9fba6af2d3d4d (tag: jdk-17.0.18+4, branch: master)
- Build type: fastdebug
- Coverage: enabled (built with --enable-coverage)
- Build environment: Linux, compiled from source with GCC 9.5
A DESCRIPTION OF THE PROBLEM :
- I observe a deterministic VM crash (fastdebug build) when running a slightly modified version of test/hotspot/jtreg/compiler/codecache/jmx/PeakUsageTest.java.
- The test allocates a code blob using WhiteBox.allocateCodeBlob and later, in a finally block, calls WhiteBox.freeCodeBlob on the same address inside a loop (freeing the same blob many times).
- Expected output: “PASS”. Actual behavior: VM aborts with an internal assertion failure in nmethod::flush:
- assert(is_zombie() || is_osr_method()) failed: must be a zombie method
- Location: src/hotspot/share/code/nmethod.cpp:1548
- Problematic frame: V [libjvm.so+0x27ba414] nmethod::flush()+0x3e4
- The crash reproduces consistently with the flags below. This suggests that repeatedly invoking WhiteBox.freeCodeBlob on an already-freed/invalid blob can drive the nmethod lifecycle into an unexpected state where nmethod::flush() runs on a non-zombie, triggering the assert. Even if the test misuses WhiteBox (double-free), the VM abort seems undesirable for WhiteBox-based testing; a safer behavior might be to make freeCodeBlob idempotent or return an error without tripping asserts.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Same as the way to run `test/hotspot/jtreg/compiler/codecache/jmx/PeakUsageTest.java`.
ACTUAL -
JRE version: OpenJDK Runtime Environment (17.0.18) (build 17.0.18-internal+0-adhoc.syc.jdk17u)
Java VM: OpenJDK 64-Bit Server VM (17.0.18-internal+0-adhoc.syc.jdk17u, mixed mode, tiered, compressed oops, compressed class ptrs, g1 gc, linux-amd64)
Problematic frame:
V [libjvm.so+0x5692ef] ciObjectFactory::create_new_metadata(Metadata*)+0x2f
---------- BEGIN SOURCE ----------
package compiler.codecache.jmx;
import jdk.test.whitebox.code.BlobType;
import java.lang.management.MemoryPoolMXBean;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class PeakUsageTest {
private final BlobType btype;
public PeakUsageTest(BlobType btype) {
this.btype = btype;
}
public static void main(java.lang.String[] args) {
for (BlobType btype : BlobType.getAvailable()) {
new PeakUsageTest(btype).runTest();
}
}
protected void runTest() {
MemoryPoolMXBean bean = btype.getMemoryPool();
bean.resetPeakUsage();
long addr = CodeCacheUtils.WB.allocateCodeBlob(CodeCacheUtils.ALLOCATION_SIZE, btype.id);
int lvInt3 = -803;
int lvInt1 = -803;
int lvInt2 = -803;
boolean lvBool1 = false;
String lvStr1 = new String("asdfasf123123");
AtomicReferenceArray<String> lvAtom1 = new AtomicReferenceArray<String>(64);
for (int tmpi = 0; tmpi < 64; tmpi++) {
lvAtom1.set(tmpi, "str" + tmpi);
}
try {
long currUsage = bean.getUsage().getUsed();
long peakUsage = bean.getPeakUsage().getUsed();
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
"Peak usage does not match usage after allocation for " + bean.getName());
} finally {
if (addr != 0) {
CodeCacheUtils.WB.freeCodeBlob(addr);
}
}
bean.resetPeakUsage();
long currUsage = bean.getUsage().getUsed();
long peakUsage = bean.getPeakUsage().getUsed();
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
"Code cache peak usage is not equal to usage after reset for " + bean.getName());
long addr2 = CodeCacheUtils.WB.allocateCodeBlob(CodeCacheUtils.ALLOCATION_SIZE, btype.id);
try {
for (lvInt1 = 0; lvInt1 < 333; lvInt1++) {
currUsage = bean.getUsage().getUsed();
peakUsage = bean.getPeakUsage().getUsed();
for (lvInt2 = 0; lvInt2 < 333; lvInt2++) {
int tmpVar1 = lvInt1 + lvInt3;
int tmpVar2 = (lvInt1 * lvInt3) - lvInt1;
lvInt1 = tmpVar1 + tmpVar2;
CodeCacheUtils.assertEQorLTE(
btype,
currUsage,
peakUsage,
("Code cache peak usage is not equal to usage after fresh "
+ "allocation for ")
+ bean.getName());
}
}
} finally {
if (addr2 != 0) {
for (lvInt1 = 0; lvInt1 < 333; lvInt1++) {
CodeCacheUtils.WB.freeCodeBlob(addr2);
lvBool1 = lvAtom1.compareAndSet(32, lvStr1, lvStr1);
}
}
}
System.out.println("PASS");
}
}
---------- END SOURCE ----------