Java 类路径在 JVM 启动后是否最终确定?

2022-09-02 01:54:33

我最近读了很多关于Java类加载过程的文章。我经常遇到一些文本,声称在运行时不可能将类添加到类路径中,并且在没有类加载器黑客(URLClassLoaders等)的情况下加载它们。

据我所知,类是动态加载的。这意味着它们的字节码表示形式仅在需要时加载并转换为java.lang.Class对象。

那么,在 JVM 启动后,难道不应该将 JAR 或 *.class 文件添加到类路径中并装入这些类(如果尚未装入这些类)吗?(需要明确的是:在这种情况下,类路径只是文件系统上的文件夹。“添加 JAR 或 *.class 文件”仅表示将它们放在此文件夹中。

如果不是,这是否意味着在JVM启动时搜索类路径,并且找到的类的所有完全限定名称都缓存在内部“列表”中?

如果你能在你的答案中指出我的一些来源,那就太好了。最好是官方的 SUN 文档:Sun JVM Spec。我已经阅读了规范,但找不到有关类路径的任何内容,以及它是否在JVM启动时完成。

附言

这是一个理论问题。我只是想知道这是否可能。没有什么实用的东西是我想实现的。只有我对知识的渴望:)


答案 1

这里有两个概念被混合在一起:类路径和类路径中的类文件。

如果将类路径指向某个目录,则通常没有问题,将文件添加到该目录并将其作为类路径的一部分进行选取。由于类路径中所有类的潜在大小,现代JVM在启动时加载它们并不可行。但是,这具有有限的价值,因为它不包括Jar文件。

但是,在正在运行的 JVM 上更改类路径本身(搜索哪些目录、jar 等)将在很大程度上取决于实现。据我所知,在标准的 Sun JVM 上,没有记录在案的(如保证工作)方法来实现这一点。

一般来说,如果这是你需要做的事情(有一个在运行时更改的动态类路径),那么你想要实现一个ClassLoader,如果没有其他原因,只能把它扔掉,并制作一个新的,如果需要卸载它们,它不再引用这些类。

但是,对于少量的动态加载,有更好的方法。在 Java 1.6 中,您可以在一个目录 (*.jar) 中指定所有 jar 文件,这样您就可以告诉用户将其他库放在指定的位置(尽管它们在启动时必须在那里)。

您还可以选择在类路径中包含 jar 文件或其他位置,即使您不需要它,也可以将其作为占位符,以便某人将可选的 jar 或资源文件(如日志配置文件)放在那里。

但是,如果您需要认真的动态类装入,特别是在应用程序运行时卸载,则需要类装入器实现。


答案 2

由于没有人能给我一个明确的答案,也没有一个指向文档相应部分的链接,我自己提供了一个答案。尽管如此,我要感谢所有试图回答这个问题的人。

简短的回答:

类路径在 JVM 启动时不是最终的。

实际上,您可以在 JVM 启动后将类放在类路径中,它们将被加载。


长答案:

为了回答这个问题,我遵循了用户未知数的建议,并编写了一个小测试程序。

基本思想是有两个类。一个是实例化第二个类的主类。启动时,第二个类不在类路径上。cli 程序启动后,它会提示您按 Enter 键。在按 Enter 键之前,请复制类路径上的第二个类。按回车键后,将实例化第二个类。如果类路径在 JVM 启动时是最终的,这将引发异常。但事实并非如此。因此,我假设类路径在 JVM 启动时不是最终的。

以下是源代码:

JVMTest.java

package jvmtest;

import java.io.Console;
import jvmtest.MyClass;

public class JVMTest {
  public static void main(String[] args) {
    System.out.println("JVMTest started ...");

    Console c = System.console();
    String enter = c.readLine("Press Enter to proceed");
    MyClass myClass = new MyClass();
    System.out.println("Bye Bye");
  }
}

我的班级.java

package jvmtest;

public class MyClass {
  public MyClass() {
    System.out.println("MyClass v2");
  }
}

文件夹结构如下所示:

jvmtest/
  JVMTest.class
  MyClass.class

我用这个命令启动了cli程序:

> java -cp /tmp/ jvmtest.JVMTest

如您所见,我在 /tmp/jvmtest 中有我的 jvmtest 文件夹。显然,您必须根据放置类的位置进行更改。

因此,以下是我执行的步骤:

  1. 确保只有 JVMTest.class 在 jvmtest 中。
  2. 使用上面的命令启动程序。
  3. 只是为了确保按回车键。您应该会看到一个异常,告诉您找不到任何类。
  4. 现在再次启动程序。
  5. 程序启动后,系统会提示您按 Enter 将 MyClass 文件复制到 jvmtest 文件夹中。
  6. 按回车键。您应该看到“MyClass v1”。

附加说明:

当我将MyClass类打包在jar中并运行上面的测试时,这也有效。

我在运行Mac OS X 10.6.3的Macbook Pro上运行了这个

> Java -version

结果:

java version "1.6.0_20"
Java(TM) SE Runtime Environment (build 1.6.0_20-b02-279-10M3065)
Java HotSpot(TM) 64-Bit Server VM (build 16.3-b01-279, mixed mode)

推荐