-
Bug
-
Resolution: Fixed
-
P3
-
11, 17, 20
Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build |
---|---|---|---|---|---|---|
JDK-8317249 | 17.0.10 | Goetz Lindenmaier | P3 | Resolved | Fixed | b01 |
There is a bug in the VM.classloaders Dcmd whereby it assumes all loaders in the parent chain must have an associated CLD, which is not true if the loader has never defined a class itself. This causes the hierarchy printing to cease at the last ancestor that does have a CLD. There is this code:
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp#L162
void print_with_childs(outputStream* st, BranchTracker& branchtracker,
bool print_classes, bool verbose) const {
ResourceMark rm;
if (_cld == NULL) {
// Not sure how this could happen: we added a preliminary node for a parent but then never encountered
// its CLD?
return;
}
But the cld will be null if this loader never defined a class itself.
Here is a test program:
TestClass.java
public class TestClass {
public static void run() {
System.out.println("Running in loader " + TestClass.class.getClassLoader());
try {
Thread.sleep(60000);
}
catch (InterruptedException ex) {}
}
}
compile it into a classes/subdirectory. Then here is the main program:
Main.java
import java.lang.reflect.*;
import java.io.*;
public class Main {
static class DelegatingLoader extends ClassLoader {
final String name;
DelegatingLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
}
public String toString() {
return "Loader " + name + " with parent: " + getParent();
}
}
static class LocalLoader extends DelegatingLoader {
LocalLoader(ClassLoader parent, String name) {
super(parent, name);
}
protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
if (name.startsWith("Test")) {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c==null) c = findClass(name);
if (resolve) resolveClass(c);
return c;
}
} else {
return super.loadClass(name,resolve);
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = name.replace('.','/').concat(".class");
String filename = "classes" + File.separator + path;
try {
FileInputStream in = new FileInputStream(filename);
byte[] bytes = new byte[in.available()];
in.read(bytes);
in.close();
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException(name,e);
}
}
}
public static void main(String[] args) throws Throwable {
ClassLoader ccl = Main.class.getClassLoader();
DelegatingLoader l1 = new DelegatingLoader(ccl, "L1");
DelegatingLoader l2 = new DelegatingLoader(l1, "L2");
LocalLoader l3 = new LocalLoader(args.length == 0 ? l2 : ccl, "L3");
Class<?> c = Class.forName("TestClass", true, l3);
Method m = c.getDeclaredMethod("run", new Class[] { });
m.invoke(null, new Object[] {});
}
}
If we run the test with an argument and then run VM.classloaders we see the expected:
> Running in loader Loader L3 with parent: jdk.internal.loader.ClassLoaders$AppClassLoader@30946e09
jcmd20 17253 VM.classloaders verbose=true fold=false
17253:
+-- <bootstrap>
|
| Loader Oop: 0x0000000000000000
| Loader Data: 0x00007f4f20135100
| Loader Klass: 0x0000000000000000
|
+-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
| Loader Oop: 0x00000007ff758890
| Loader Data: 0x00007f4f2014e9e0
| Loader Klass: 0x0000000800096698
|
+-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
|
| Loader Oop: 0x00000007ff758fb0
| Loader Data: 0x00007f4f20292020
| Loader Klass: 0x0000000800095d68
|
+-- Main$LocalLoader
Loader Oop: 0x00000004540208c0
Loader Data: 0x00007f4f202c0610
Loader Klass: 0x0000000801000cf8
but if we run with no args so that the purely delegating loader is inserted into the hierarchy we see:
> Running in loader Loader L3 with parent: Loader L2 with parent: Loader L1 with parent: jdk.internal.loader.ClassLoaders$AppClassLoader@30946e09
> jcmd20 16903 VM.classloaders verbose=true fold=false
16903:
+-- <bootstrap>
|
| Loader Oop: 0x0000000000000000
| Loader Data: 0x00007f1018135100
| Loader Klass: 0x0000000000000000
|
+-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
| Loader Oop: 0x00000007ff758890
| Loader Data: 0x00007f101814e9e0
| Loader Klass: 0x0000000800096698
|
+-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
|
| Loader Oop: 0x00000007ff758fb0
| Loader Data: 0x00007f101828a130
| Loader Klass: 0x0000000800095d68
|
and the report terminates with the "app" loader.
https://github.com/openjdk/jdk/blob/master/src/hotspot/share/classfile/classLoaderHierarchyDCmd.cpp#L162
void print_with_childs(outputStream* st, BranchTracker& branchtracker,
bool print_classes, bool verbose) const {
ResourceMark rm;
if (_cld == NULL) {
// Not sure how this could happen: we added a preliminary node for a parent but then never encountered
// its CLD?
return;
}
But the cld will be null if this loader never defined a class itself.
Here is a test program:
TestClass.java
public class TestClass {
public static void run() {
System.out.println("Running in loader " + TestClass.class.getClassLoader());
try {
Thread.sleep(60000);
}
catch (InterruptedException ex) {}
}
}
compile it into a classes/subdirectory. Then here is the main program:
Main.java
import java.lang.reflect.*;
import java.io.*;
public class Main {
static class DelegatingLoader extends ClassLoader {
final String name;
DelegatingLoader(ClassLoader parent, String name) {
super(parent);
this.name = name;
}
public String toString() {
return "Loader " + name + " with parent: " + getParent();
}
}
static class LocalLoader extends DelegatingLoader {
LocalLoader(ClassLoader parent, String name) {
super(parent, name);
}
protected Class<?> loadClass(String name,boolean resolve) throws ClassNotFoundException {
if (name.startsWith("Test")) {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c==null) c = findClass(name);
if (resolve) resolveClass(c);
return c;
}
} else {
return super.loadClass(name,resolve);
}
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
String path = name.replace('.','/').concat(".class");
String filename = "classes" + File.separator + path;
try {
FileInputStream in = new FileInputStream(filename);
byte[] bytes = new byte[in.available()];
in.read(bytes);
in.close();
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
throw new ClassNotFoundException(name,e);
}
}
}
public static void main(String[] args) throws Throwable {
ClassLoader ccl = Main.class.getClassLoader();
DelegatingLoader l1 = new DelegatingLoader(ccl, "L1");
DelegatingLoader l2 = new DelegatingLoader(l1, "L2");
LocalLoader l3 = new LocalLoader(args.length == 0 ? l2 : ccl, "L3");
Class<?> c = Class.forName("TestClass", true, l3);
Method m = c.getDeclaredMethod("run", new Class[] { });
m.invoke(null, new Object[] {});
}
}
If we run the test with an argument and then run VM.classloaders we see the expected:
> Running in loader Loader L3 with parent: jdk.internal.loader.ClassLoaders$AppClassLoader@30946e09
jcmd20 17253 VM.classloaders verbose=true fold=false
17253:
+-- <bootstrap>
|
| Loader Oop: 0x0000000000000000
| Loader Data: 0x00007f4f20135100
| Loader Klass: 0x0000000000000000
|
+-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
| Loader Oop: 0x00000007ff758890
| Loader Data: 0x00007f4f2014e9e0
| Loader Klass: 0x0000000800096698
|
+-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
|
| Loader Oop: 0x00000007ff758fb0
| Loader Data: 0x00007f4f20292020
| Loader Klass: 0x0000000800095d68
|
+-- Main$LocalLoader
Loader Oop: 0x00000004540208c0
Loader Data: 0x00007f4f202c0610
Loader Klass: 0x0000000801000cf8
but if we run with no args so that the purely delegating loader is inserted into the hierarchy we see:
> Running in loader Loader L3 with parent: Loader L2 with parent: Loader L1 with parent: jdk.internal.loader.ClassLoaders$AppClassLoader@30946e09
> jcmd20 16903 VM.classloaders verbose=true fold=false
16903:
+-- <bootstrap>
|
| Loader Oop: 0x0000000000000000
| Loader Data: 0x00007f1018135100
| Loader Klass: 0x0000000000000000
|
+-- "platform", jdk.internal.loader.ClassLoaders$PlatformClassLoader
|
| Loader Oop: 0x00000007ff758890
| Loader Data: 0x00007f101814e9e0
| Loader Klass: 0x0000000800096698
|
+-- "app", jdk.internal.loader.ClassLoaders$AppClassLoader
|
| Loader Oop: 0x00000007ff758fb0
| Loader Data: 0x00007f101828a130
| Loader Klass: 0x0000000800095d68
|
and the report terminates with the "app" loader.
- backported by
-
JDK-8317249 Dcmd VM.classloaders fails to print the full hierarchy
-
- Resolved
-