【问题标题】:What loads the java system classloader?什么加载了java系统类加载器?
【发布时间】:2012-07-08 20:28:05
【问题描述】:

我们知道我们可以覆盖系统classloader

java -Djava.system.class.loader=com.test.MyClassLoader xxx

那么,既然com.test.MyClassLoader本身就是一个类,那么它是由谁加载的呢?

我们如何获取这个“元”类加载器的类文件?

【问题讨论】:

  • Bootstrap 加载自己和其他人 - javabench.in/2012/02/java-class-loading.html
  • “有趣”的边缘案例:在自定义系统 ClassLoaderMySysCl 下,它不会将应用程序类路径类的加载委托给其父级 MySysCl.class.getClassLoader().getClass().equals(ClassLoader.getSystemClassLoader().getClass()) == true。 :)

标签: java classloader


【解决方案1】:

Bootstrap classloader 是所有classloaders 的父级,并在 JRE 的 lib 目录(rt.jar 和 i18n.jar)中加载标准 JDK 类。所有的 java.* 类都由这个classloader 加载。

Extensions Classloader 是 Bootstrap 类加载器的直接子代。该类加载器加载 JRE 的 lib\ext 目录中的类。

System-Classpath classloader 是 Extensions 类加载器的直接子代。它加载由CLASSPATH 环境变量指定的类和jar

您可以尝试通过“java.system.class.loader”属性注入您的自定义类加载器(参见ClassLoader#getSystemClassLoader)。

Default System class loader 是 MyClassLoader 实例的父级,

【讨论】:

    【解决方案2】:

    来自ClassLoader.getSystemClassLoader 的 Javadoc:

    如果在第一次调用此方法时定义了系统属性“java.system.class.loader”,则该属性的值将被视为将作为系统类加载器返回的类的名称。 该类使用默认的系统类加载器加载,并且必须定义一个公共构造函数,该构造函数接受一个 ClassLoader 类型的参数,用作委托父级。

    默认的系统类加载器本身是特定于 JVM 实现的。

    【讨论】:

    • 那么,我们com.test.MyClassLoader 的父级是默认的系统类加载器?仍然使用默认的系统类加载器?即使我们覆盖了系统类加载器?
    • @James.Xu:是的,父级仍然是默认的系统类加载器,但您始终可以覆盖loadClass 实现以绕过它。
    • 请参阅stackoverflow.com/q/5380275/632951,了解有关如何使用代码而不是 cmd 设置执行此操作的更多信息。
    • 总是先参考文档,我每次在 SO 中阅读正确答案时都会重新学习。
    【解决方案3】:

    Tldr:

    § ..该属性的值被视为将作为系统类加载器返回的类的名称。使用默认系统类加载器..

    加载该类

    ..因此,如果您的类加载器将 X 替换为系统类加载器,那么您的类加载器的父级将是 X,即默认的系统类加载器。

    (X 的类型类似于sun.misc.Launcher$AppClassLoader。)

    更多信息可以在docs.oracle.com - How the Java Launcher Finds Classes找到:

    Java 启动器 java 启动 Java 虚拟机。这 虚拟机按以下顺序搜索和加载类:

    Bootstrap 类 - 构成 Java 平台的类,包括 rt.jar 和其他几个重要 jar 文件中的类。

    扩展类 - 使用 Java 扩展机制的类。这些被捆绑为位于扩展目录中的 .jar 文件。

    用户类 - 由开发人员和第三方定义的不利用扩展机制的类。你确定 这些类的位置使用 -classpath 选项 命令行或使用 CLASSPATH 环境变量。

    Tsmr:

    我们可以证明X确实是我们Classloader的父级:

    /** run with -Djava.system.class.loader=MyCL to use this classloader */
    public class MyCL extends ClassLoader {
        public MyCL(ClassLoader parent) { // this constructor must be public, else IllegalAccessException
            super(parent);
        }
    }
    

    这是我们的主要代码:

    public class Main {
        public static void main(String args[]) {
            System.out.println("getSystemClassLoader(): " + ClassLoader.getSystemClassLoader());
    
            ClassLoader cl = MyCL.class.getClassLoader();
            System.out.println("Classloader of MyCL: " + cl);
    
            Class type_of_cl = cl.getClass();
            System.out.println("..and its type: " + type_of_cl);
    
            ClassLoader cl_of_cl = class_of_cl.getClassLoader();
            System.out.println("Classloader of (Classloader of MyCL): " + cl_of_cl);
        }
    }
    

    这是使用命令java -Djava.system.class.loader=MyCL Main(参见Eclipse run config)运行时的输出(在我的系统上):

    getSystemClassLoader(): MyCL@1888759
    MyCL 的类加载器:sun.misc.Launcher$AppClassLoader@7fdcde
    ..及其类型:类 sun.misc.Launcher$AppClassLoader
    (MyCL的Classloader)的类加载器:null

    我们可以看到MyCL的类加载器是sun.misc.Launcher$AppClassLoader,这是系统默认的类加载器。

    (根据上面 Oracle 的其他引用中所见的语言,默认系统类加载器也称为“用户类”的类加载器。为同一件事想出 2 个名称的 Oracle。 )

    【讨论】:

      【解决方案4】:

      适当的答案是:

      这也澄清了原来的问题。

      当你这样做时,

      java -Djava.system.class.loader=com.test.MyClassLoader xxx

      -D 选项用于在作为 JVM 启动的一部分加载的 java.lang.System 实例的 Properties 对象中设置系统属性。该选项仅更改内存中的属性。在下一次调用 XXX 类或其他一些类时,将再次加载作为 System 类一部分的默认属性。在这里,您已将 java.system.class.loader 属性设置为值 com.test.MyClassLoader。换句话说,您想用新的系统类加载器覆盖默认系统类加载器(也称为引导类加载器),以便调用您的类 XXX。作为 JVM 启动的一部分加载的默认系统类加载器将查找内存中的 java.system.class.loader 属性以查找覆盖系统类加载器的名称 - 如果设置(在您的情况下,设置为MyClassLoader)——并从你的类路径加载它,默认情况下,它是你的工作目录的根目录(应该包含 XXX)或类路径变量中的一个,作为 -cp 或作为环境变量 CLASSPATH,如果已定义。

      如果你写在你的类XXX的main方法中

          System.out.println(ClassLoader.getSystemClassLoader());
          System.out.println(MyClassLoader.class.getClassLoader());
      

      你应该看到

        MyClassLoader      
      

      (加载XXX的新系统类加载器)

       sun.misc.Launcher$AppClassLoader     
      

      (加载新系统类加载器的默认系统类加载器)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-01-04
        • 2011-09-30
        • 2023-03-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-10-18
        • 1970-01-01
        相关资源
        最近更新 更多