RFR: 8281853 serviceability/sa/ClhsdbThreadContext.java failed with NullPointerException: Cannot invoke "sun.jvm.hotspot.gc.shared.GenCollectedHeap.getGen(int)" because "this.heap" is null
Chris Plummer
cjplummer at openjdk.java.net
Sat Mar 19 21:39:46 UTC 2022
`isInNewGen()` is throwing an NPE because "heap" is null:
public boolean isInNewGen() {
return ((gen != null) && (gen == ((GenCollectedHeap)heap).getGen(0)));
}
The call came from here:
} else if (isInHeap()) {
if (isInTLAB()) {
tty.print("In thread-local allocation buffer for thread (");
getTLABThread().printThreadInfoOn(tty);
tty.print(") ");
getTLAB().printOn(tty); // includes "\n"
} else {
if (isInNewGen()) {
tty.print("In new generation ");
} else if (isInOldGen()) {
tty.print("In old generation ");
} else {
tty.print("In unknown section of Java heap");
}
if (getGeneration() != null) {
getGeneration().printOn(tty); // does not include "\n"
}
tty.println();
}
`isInHeap()` returns true if either "heap" or "gen" is non-null. If "gen" is non-null and "heap" is null, it is not safe to call `isInNewGen()`. Yet you can see from the code above that when `isInNewGen()` is called, the only guarantee is that "heap" or "gen" is non-null, not both, and it turns out that `PointerFinder.find()` only sets "gen" when the ptr is found to be in a generation of the heap. It does not set "heap". So the logic in `PointerFinder.find()` is not in agreement with the logic in `PointerLocation.printOn()` w.r.t. to the setting up and meaning of the "gen" and "heap" fields.
The solution is pretty straight forward. Always set "heap" when the ptr is in the heap, even if it is in a generation (in which case "gen" is also set) or in a tlab (in which case "tlab" is also set). `isInHeap()` no longer requires that "gen" also be set, just "heap".
I also noticed that the printlns in the following were not being triggered:
if (isInNewGen()) {
tty.print("In new generation ");
} else if (isInOldGen()) {
tty.print("In old generation ");
} else {
This was true even though "gen" was set and was indeed either pointing to the new gen or old gen. It turns out that the following returns a newly created Generation object:
` ((GenCollectedHeap)heap).getGen(0)`
For this reason `equals()` must be used to compare them, not ==. The `VMObject.equals()` method is what ends up being called, and it compares the address associated with the underlying `VMObject`.
-------------
Commit messages:
- Fix isInOldGen() and isInNewGen(). The instances are not the same so == cannot be used to compare. Instead uses the VMObject.equals() method which will compare the VMObject.addr values for each.
- fix for 8281853
Changes: https://git.openjdk.java.net/jdk/pull/7873/files
Webrev: https://webrevs.openjdk.java.net/?repo=jdk&pr=7873&range=00
Issue: https://bugs.openjdk.java.net/browse/JDK-8281853
Stats: 5 lines in 2 files changed: 1 ins; 1 del; 3 mod
Patch: https://git.openjdk.java.net/jdk/pull/7873.diff
Fetch: git fetch https://git.openjdk.java.net/jdk pull/7873/head:pull/7873
PR: https://git.openjdk.java.net/jdk/pull/7873
More information about the serviceability-dev
mailing list