【问题标题】:The classes loaded by Bootstrap ClassloaderBootstrap Classloader 加载的类
【发布时间】:2013-12-27 07:16:47
【问题描述】:

这个问题是this post提出的。当运行如下简单程序时

public class Sample
{
    public static void main(String[] args)
    {
    }
}

带有选项-versbose:class,它在加载这个类之前列出了一堆类

[Opened C:\jdk1.6.0_14\jre\lib\rt.jar]
[Loaded java.lang.Object from C:\jdk1.6.0_14\jre\lib\rt.jar]
[Loaded java.io.Serializable from C:\jdk1.6.0_14\jre\lib\rt.jar]
[Loaded java.lang.Comparable from C:\jdk1.6.0_14\jre\lib\rt.jar]
.
.
.
.
.
.
[Loaded java.security.cert.Certificate from C:\jdk1.6.0_14\jre\lib\rt.jar]
[Loaded Sample from file:/D:/tmp/]
[Loaded java.lang.Shutdown from C:\jdk1.6.0_14\jre\lib\rt.jar]
[Loaded java.lang.Shutdown$Lock from C:\jdk1.6.0_14\jre\lib\rt.jar]

我的问题是,

我的程序从不需要 java.util.CollectionSet List 等类。那为什么 Bootstrap 类加载器正在加载它们。这是 JVM 规范要求的方式还是 Bootstrap 类加载器如何决定加载哪些类?

编辑:

另一方面:

即使你尝试运行一个不存在的类,程序也会以ClassNotFoundException 结束,但并非没有加载前面提到的所有类。因此,这些类仅在调用 JVM 时加载!所以 JVM 默认会加载一组类,但是,是什么控制了这种行为

【问题讨论】:

    标签: java classloader


    【解决方案1】:

    Classjava.lang 包的一部分,因此引导类加载器会选择它进行加载,但Class 本身需要一些依赖的框架类,例如ListSet,因此它们也会被加载.

    如果你会在 JDK 中看到 Class 的代码,你会发现以下导入

    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.Iterator;
    import java.util.List;
    import java.util.LinkedList;
    import java.util.LinkedHashSet;
    import java.util.Set;
    import java.util.Map;
    import java.util.HashMap;
    

    Class源内部使用Collections,所以需要加载。

    【讨论】:

    • 但是即使您尝试运行不存在的类,也会加载相同的类集!关键是,这些类是通过简单地调用 JVM 来加载的。
    • 是的,因为 java.lang 包类是由引导类加载器自动加载的,所有依赖类也是如此。无论您键入不存在或存在的类名。
    • 这也是我们不需要在源代码中导入 java.lang 导入的原因,因为它们是预加载的。
    • 但是按照这个逻辑,即使是来自CollectionIO 的类也不应该被要求导入!
    • 是的,但是 Java 人员无法跟踪加载了其他包中的哪些类并为它们提供默认导入。因为有些可能会被删除,有些可能会在将来添加。然后它将变得依赖于实现。
    【解决方案2】:

    似乎对什么是引导类加载器有一些误解:

    • 它解决了鸡/蛋问题:要拥有一个类加载器实例,您需要 - 很多 - 类,包括 object/classs/collections/io/nio 等
    • 又名VM's built-in classloader
    • 它加载所有java.类,即以java开头的类。不能被其他任何东西加载(这是安全模型的一部分)
    • 实际上,bootstap 类加载器会加载 JRE 提供的所有类(例如来自 rt.jar 的任何类),尽管如果包不使用 java 启动。可以通过应用程序类加载器加载它
    • object.getClass().getClassLoader() 返回 null,因为引导类加载器通常位于本机/C 代码(JVM 的一部分)中,即它不是 java 类(因为它不能自行加载,请参见上面的 chicken/egg)
    • 它不是任何其他类加载器的直接父级,ClassLoader.getParent() 不会返回它。
    • 没有正式的直接方式来调用它的方法(因为它不作为 java 类存在),

    现在回答直接问题:“是什么支配了这种行为?” - 当第一次尝试通过“Class.forName”加载一个类时,通常当前类的类加载器会要求它自己的父类这样做。引导类加载器被认为是系统类加载器(加载主类)的父级。如果当前类已经被引导类加载器加载,它会使用 VM 来解析需要加载的类。

    最后 - java 中的类总是在需要时动态加载,每次尝试调用方法时 - 它从必须解析的常量池中获取完全限定的类名。该行为独立于类的类加载器。因此,当 JVM 实现第一个类时,球开始滚动。

    【讨论】:

      【解决方案3】:

      JRE 可能会延迟加载,即仅在需要时加载一个类。

      但是,在访问主类之前,JRE 已经在执行许多其他 java 代码,特别是 sun.misc.Launcher。这就是为什么在你的类之前加载很多类的原因。


      一般来说,JRE 可以自行决定在何时加载它喜欢的任何类。

      http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.1.2

      一个实现可能会解析来自很早就被链接的类或接口的符号引用,甚至可以递归地解析来自被进一步引用的类和接口的所有符号引用。

      一个实现可能会选择仅在积极使用符号引用时才解析它;对所有符号引用一致使用此策略将代表“最懒惰”的解决形式。

      请注意,类的初始化发生在严格指定的时刻;没有“急切”的初始化——http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-07-05
        • 1970-01-01
        • 1970-01-01
        • 2019-09-25
        • 2014-08-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多