ADDITIONAL SYSTEM INFORMATION :
This bug produce on OSX and linux, haven't test other OSes.
Affect java versions: 11 to 17, haven't test older versions or versions after 17.
A DESCRIPTION OF THE PROBLEM :
Service Ablity offers function which can traverse all loaded classes using VM.getVM().getClassLoaderGraph(). I write an attacher to test this method, but the attacher always exits abnormally. The attacher code is listed in source code part of this bug report. The attacher exits after throwing WrongTypeException, see the actual result part below, I paste full stack trace there.
This bug seems to be HotspotAgent consider the dictionary field of ClassLoaderData is static.
public class ClassLoaderData extends VMObject {
static {
VM.registerVMInitializedObserver(new java.util.Observer() {
public void update(java.util.Observable o, Object data) {
initialize(VM.getVM().getTypeDataBase());
}
});
}
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("ClassLoaderData");
// omit for simplicity
dictionaryField = type.getAddressField("_dictionary");
}
}
in the above code, dictionary is assigned using type.getAddressField which is actually a BasicAddressFieldWrapper. The getValue method of the wrapper will call BasicField.getAddress method which will check if the field is static. But dictionary field of ClassloaderData is not static, according to source code:
// ClassLoaderData.hpp
class ClassLoaderData : public CHeapObj<mtClass> {
Dictionary* _dictionary; // The loaded InstanceKlasses, including initiated by this class loader
}
the attacher then throws WrongTypeException and exits.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
First run any java program, use jps command to get pid (assume it is 35278) of this process.
run following command to compile source code:
javac --add-exports jdk.hotspot.agent/sun.jvm.hotspot.classfile=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED --add-modules=jdk.hotspot.agent SATest.java
then run following command to attach to the running process:
java --add-exports jdk.hotspot.agent/sun.jvm.hotspot.classfile=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED --add-modules=jdk.hotspot.agent SATest 35278
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The SATest program should print all classes and their classloader, then exit normally.
ACTUAL -
The SATest process exits because of WrongTypeException.
Exception in thread "main" sun.jvm.hotspot.types.WrongTypeException
at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicField.getAddress(BasicField.java:246)
at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicAddressFieldWrapper.getValue(BasicAddressFieldWrapper.java:48)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderData.dictionary(ClassLoaderData.java:64)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderData.allEntriesDo(ClassLoaderData.java:120)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderDataGraph.allEntriesDo(ClassLoaderDataGraph.java:92)
at SATest.main(SATest.java:11)
---------- BEGIN SOURCE ----------
import sun.jvm.hotspot.HotSpotAgent;
import sun.jvm.hotspot.runtime.VM;
public class SATest {
public static void main(String[] args) throws Exception {
HotSpotAgent hotSpotAgent = new HotSpotAgent();
int pid = Integer.parseInt(args[0]);
hotSpotAgent.attach(pid);
VM.getVM().getClassLoaderDataGraph().allEntriesDo(
(klass, loader) -> {
System.out.println(klass.getName() + " " + loader);
}
);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I've found two workarounds:
1. use ClassloaderGraph.classesDo method instead.
This method offers similar functionality but does not throw exception.
2. change hotspot source code and rebuild jvm
// ClassLoaderData.java
public class ClassLoaderData extends VMObject {
public Dictionary dictionary() {
Address addr = dictionaryField.getValue(getAddress());
if (addr == null) {
System.out.println("_dictionary is null " + getAddress());
return null;
}
return new Dictionary(addr)
}
}
The BasicAddressFieldWrapper has another getValue method which accepts an Address parameter. This version of method checks if the specified field is nonStatic, and this seems to be the expected callee method.
FREQUENCY : always
This bug produce on OSX and linux, haven't test other OSes.
Affect java versions: 11 to 17, haven't test older versions or versions after 17.
A DESCRIPTION OF THE PROBLEM :
Service Ablity offers function which can traverse all loaded classes using VM.getVM().getClassLoaderGraph(). I write an attacher to test this method, but the attacher always exits abnormally. The attacher code is listed in source code part of this bug report. The attacher exits after throwing WrongTypeException, see the actual result part below, I paste full stack trace there.
This bug seems to be HotspotAgent consider the dictionary field of ClassLoaderData is static.
public class ClassLoaderData extends VMObject {
static {
VM.registerVMInitializedObserver(new java.util.Observer() {
public void update(java.util.Observable o, Object data) {
initialize(VM.getVM().getTypeDataBase());
}
});
}
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("ClassLoaderData");
// omit for simplicity
dictionaryField = type.getAddressField("_dictionary");
}
}
in the above code, dictionary is assigned using type.getAddressField which is actually a BasicAddressFieldWrapper. The getValue method of the wrapper will call BasicField.getAddress method which will check if the field is static. But dictionary field of ClassloaderData is not static, according to source code:
// ClassLoaderData.hpp
class ClassLoaderData : public CHeapObj<mtClass> {
Dictionary* _dictionary; // The loaded InstanceKlasses, including initiated by this class loader
}
the attacher then throws WrongTypeException and exits.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
First run any java program, use jps command to get pid (assume it is 35278) of this process.
run following command to compile source code:
javac --add-exports jdk.hotspot.agent/sun.jvm.hotspot.classfile=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED --add-modules=jdk.hotspot.agent SATest.java
then run following command to attach to the running process:
java --add-exports jdk.hotspot.agent/sun.jvm.hotspot.classfile=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.oops=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot=ALL-UNNAMED --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED --add-modules=jdk.hotspot.agent SATest 35278
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The SATest program should print all classes and their classloader, then exit normally.
ACTUAL -
The SATest process exits because of WrongTypeException.
Exception in thread "main" sun.jvm.hotspot.types.WrongTypeException
at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicField.getAddress(BasicField.java:246)
at jdk.hotspot.agent/sun.jvm.hotspot.types.basic.BasicAddressFieldWrapper.getValue(BasicAddressFieldWrapper.java:48)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderData.dictionary(ClassLoaderData.java:64)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderData.allEntriesDo(ClassLoaderData.java:120)
at jdk.hotspot.agent/sun.jvm.hotspot.classfile.ClassLoaderDataGraph.allEntriesDo(ClassLoaderDataGraph.java:92)
at SATest.main(SATest.java:11)
---------- BEGIN SOURCE ----------
import sun.jvm.hotspot.HotSpotAgent;
import sun.jvm.hotspot.runtime.VM;
public class SATest {
public static void main(String[] args) throws Exception {
HotSpotAgent hotSpotAgent = new HotSpotAgent();
int pid = Integer.parseInt(args[0]);
hotSpotAgent.attach(pid);
VM.getVM().getClassLoaderDataGraph().allEntriesDo(
(klass, loader) -> {
System.out.println(klass.getName() + " " + loader);
}
);
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I've found two workarounds:
1. use ClassloaderGraph.classesDo method instead.
This method offers similar functionality but does not throw exception.
2. change hotspot source code and rebuild jvm
// ClassLoaderData.java
public class ClassLoaderData extends VMObject {
public Dictionary dictionary() {
Address addr = dictionaryField.getValue(getAddress());
if (addr == null) {
System.out.println("_dictionary is null " + getAddress());
return null;
}
return new Dictionary(addr)
}
}
The BasicAddressFieldWrapper has another getValue method which accepts an Address parameter. This version of method checks if the specified field is nonStatic, and this seems to be the expected callee method.
FREQUENCY : always
- relates to
-
JDK-8269962 SA has unused Hashtable, Dictionary classes
-
- Resolved
-