【问题标题】:Flow of class loading for a simple program一个简单程序的类加载流程
【发布时间】:2013-12-27 06:35:52
【问题描述】:

我现在才刚刚开始学习 Java 的内部架构。我已经大致理解了类加载的概念,它在jvm 运行时加载所需的类,在找不到类时抛出ClassNotFoundException,并且特定的类加载器会加载该类引用的类。

谁能在下面的示例Java代码中清楚地解释类加载的流程,即引导类加载和用户定义类加载的顺序。

import java.io.File;
public class Sample
{
    public static void main(String[] args)
    {
        String fileName = "sample";
        File file = new File(fileName);
        file.isFile();
    }
} 

我还从参考资料中了解到“classloader 维护它加载的类的命名空间”。通过命名空间,这是否意味着类的字面名称?也有人可以解释一下它的含义/优势吗?

【问题讨论】:

标签: java classloader


【解决方案1】:

您将按如下方式运行Sample 课程

> java Sample

对于小魔法,查看-verbose: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]

您会看到\jre\lib\rt.jar 中的一堆类已加载,远远早于Bootstrap 类加载器(或 Primordial )加载您的类。这些是运行任何由 Bootstrap 加载的 Java 程序的先决条件。

另一组 jars 由 Extension 类加载器加载。在这个特定的示例中,不需要库 \jre\lib\ext 中的任何类,因此它没有被加载。但是扩展类加载器专门分配了从扩展库加载类的任务。

编辑:除了标准的平台 java 类,Sun/Oracle 还提供了一组用于扩展平台的核心 API 的 jar。放置在扩展 lib 文件夹中的 jar 会自动放置在类路径中,因此不需要明确包含在类路径中。这是同一主题的nice official article

最后,在 Bootstrap 和 Extension 完成加载后,Application 类加载器会加载您的类 Sample

【讨论】:

  • @Santhosh:谢谢..你的回答就足够了,但你还能更详细地介绍扩展类加载器吗?它的效用尚不清楚
  • @santosh +1 提到了-verbose:class 选项。
【解决方案2】:

Classloader hierarchy

每当一个新的 JVM 启动时,引导类加载器负责首先将关键的 Java 类(来自java.lang 包)和其他运行时类加载到内存中。引导类加载器是所有其他类加载器的父级。因此,它是唯一没有父母的。

接下来是扩展类加载器。它将引导类加载器作为父类,负责从保存在 java.ext.dirs 路径中的所有 .jar 文件中加载类——无论 JVM 的类路径如何,这些文件都可用。

开发人员的角度来看,第三个也是最重要的类加载器是系统类路径类加载器,它是扩展类加载器的直接子级。它从CLASSPATH 环境变量、java.class.path 系统属性或-classpath 命令行选项指定的目录和jar 文件中加载类。

类加载器命名空间

在 Java 中,一个类是唯一标识的 ClassLoader + Class 因为同一个类可能被两个不同的类加载器加载。

Class A loaded by ClassLoader A != Class A loaded by ClassLoader B

它有什么帮助?

有助于为不同的类加载器定义不同的保护和访问策略。以使用不同类加载器加载的小程序为例,您不希望第三方应用程序全部访问您的资源。因此,为了安全起见,维护不同的命名空间很重要。

【讨论】:

  • 这在一定程度上让我明白了一些事情。你能回答我关于类加载器命名空间的问题吗?
  • 我已经添加了解释。希望对您有所帮助。
  • 如何加载引导类加载器 - stackoverflow.com/questions/18214174/…
【解决方案3】:

JVM 在加载类的 permgen 区域维护一个运行时池。每当引用一个类时,默认类加载器会在类路径中找到该类并将其加载到此池中。这并不特定于用户定义的类或 JDK 中提供的类。当一个类被引用时,它被加载到内存中。

ClassLoader 加载的类内部存储在 ClassLoader 实例中

// The classes loaded by this class loader. The only purpose of this table
// is to keep the classes from being GC'ed until the loader is GC'ed.
private final Vector<Class<?>> classes = new Vector<>();

当需要将类添加到内存中时,调用以下函数:

// Invoked by the VM to record every loaded class with this loader.
void addClass(Class c) {
    classes.addElement(c);
}

找到了一个关于类加载器如何工作的有用图表。

【讨论】:

  • 你的意思是说——“只要它的类加载器存在,这个类就存在”??
  • 是的,ClassLoader 实例不会被 GC,因为它被 JVM 线程引用。事实上,这就是为什么即使你有一个 Singleton 类,也可以使用两个不同的类加载器创建两个实例。
  • 所以.. 你的意思是说“如果我有数百个类被同一个类加载器加载,那么在类加载器被 GC 之前,这些类将不会被 GC”??......如何这效率高吗?
  • 没有 ClassLoader 实例与堆中的任何其他对象相同。它没有被 GCed 的声明来自这样一个事实,即 ClassLoader 具有来自 JVM 线程的引用,这些线程一直运行到 Java 进程完成并且 JVM 关闭。例如,BootStrap 类加载器是一个本地实现,这意味着它的代码嵌入在 JVM 中。所以它的参考将永远存在。因此,我们说他们不是 GC 的潜在候选人。除了 GC 对它们的处理方式相同。
  • @TheLostMind 我不能肯定地说,但答案中发布的 Aniket 代码暗示 GC 对待它们没有区别;否则类加载器不需要保留Vector 以防止垃圾收集。
【解决方案4】:

Java 虚拟机 通过使用引导类加载器 (§5.3.1) 创建一个初始类来启动,该类以依赖于实现的方式指定。 Java 虚拟机然后链接初始类,初始化它和其中声明的静态实例变量,最后调用 公共类方法 void main(String[])。调用此方法会驱动所有进一步的执行。执行构成主要方法的 Java 虚拟机指令可能会导致附加类和接口的链接(并因此创建),以及调用附加方法。

阅读this链接

【讨论】:

    【解决方案5】:

    加载过程可以看成是Classloader之间的交互 子系统和JVM的内存区域。

    类加载器按三个一般步骤工作:1.) 加载 2.) 链接和 3.) 初始化。

    类加载器子系统和 内存区域发生在链接(除了其他交互!)

    链接活动细分为 i.) 验证 ii.) 准备和 iii.) 解决。 验证:更安全,检查有效编译。在步骤 ii.) 准备中 - 分配静态变量内存并分配默认值。 而在

    iii.) 解决:符号引用被替换为原始引用 来自“方法区”的引用,其中包含类级别数据和 静态变量。

    JVM Arch

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-20
      • 1970-01-01
      • 2014-04-08
      • 1970-01-01
      • 2017-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多