Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8293156

Dcmd VM.classloaders fails to print the full hierarchy

    XMLWordPrintable

Details

    • svc
    • 11
    • b17

    Backports

      Description

        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.

        Attachments

          Issue Links

            Activity

              People

                stuefe Thomas Stuefe
                dholmes David Holmes
                Votes:
                0 Vote for this issue
                Watchers:
                5 Start watching this issue

                Dates

                  Created:
                  Updated:
                  Resolved: