何时加载 Java 类?

2022-09-03 14:03:00

我在互联网上搜索了几个小时,无法得出任何结论。

最近我决定将 BouncyCastle 用于 SSL,但我希望它默认关闭,以便 BouncyCastle jar 可能不在类路径中。

private void enableBouncyCastleForSSL() {
   if (config.isBouncyCastleEnabled()) {
        Security.insertProviderAt(new BouncyCastleProvider(), 1);
    }
} 

即使禁用了config,它也在寻找BouncyCastle,并且由于类装入器错误而失败。java.lang.NoClassDefFoundError: org/bouncycastle/jce/provider/BouncyCastleProvider

我尝试只移动一行Security.insertProviderAt(new BouncyCastleProvider(), 1);对于一种新的方法,它表现出同样的问题。

但是当我引入一个类并将BouncyCastle移动到其中时,当配置被禁用时,类装入器问题不会出现

private void setupSSLProvider() {
    if (voldemortConfig.isBouncyCastleEnabled()) {
        SetupSSLProvider.useBouncyCastle();
    }
}
public class SetupSSLProvider {
  public static void useBouncyCastle() {
    Security.insertProviderAt(new BouncyCastleProvider(), 1);
  }
}

一些文章声称类仅在首次使用时才加载。http://www.programcreek.com/2013/01/when-and-how-a-java-class-is-loaded-and-initialized/

显然,在我的情况下,Java8加载了类中引用的类。

所以我的理解是,Java在执行类中的第一行代码之前,会加载一个层次的类。是吗?


答案 1

这个问题没有简单的答案。该规范为不同的实现策略留出了空间,即使在一个实现中,它也取决于类的使用方式。此外,你的问题更多的是关于“它什么时候会失败”,这取决于它为什么会失败,例如,一个类可能在某个时间点被加载以验证它的存在,但在以后实际首次使用时初始化。在这两个时间点,操作都可能失败,从而导致 .NoClassDefFoundError

我们可以将引用的类分为三组:

  • 必须在链接时解析的类(例如超类)
  • 必须在验证时装入的类
  • 其加载可以推迟到其首次实际使用的类

在您的例子中,必须在验证时加载,而不是第一次实际使用,因为您将 as 参数的结果传递给方法,并且验证程序必须检查该类是否实际扩展了方法的形式参数要求的类型。BouncyCastleProvidernew BouncyCastleProvider()Security.insertProviderAt(…)Provider

当验证发生时,也是特定于实现的,因为至少允许以下可能性:

  • 热切地遍历所有引用的类
  • 在包含类的加载或其首次使用时
  • 在包含方法的首次使用时
  • 在执行违规指令之前

Oracle的JVM更喜欢方法的首次使用,请阅读:在方法条目处验证它,因此,将调用移动到另一个不会执行的方法中,无论它是否在另一个类中,在你的情况下就足够了。

但是要与其他JVM兼容,将其移动到另一个类中更安全,但是为了遵守所有可能的JVM,您甚至需要通过Reflection加载这个其他类,以避免可以通过热切的实现遍历的直接引用(或者首先通过反射方式实例化)。BouncyCastleProviderClass.forName("… .BouncyCastleProvider").newInstance()


答案 2

推荐