什么时候加载Java类?

时间:2015-12-14 03:21:21

标签: java java-8 classloader openjdk

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

最近我决定使用BouncyCastle进行SSL,但我希望它默认关闭,这样BouncyCastle jar可能不在类路径中。

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

即使配置被禁用,它也在寻找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);
  }
}

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

显然在我的例子中,Java8加载了类中引用的类。

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

1 个答案:

答案 0 :(得分:9)

这个问题没有简单的答案。规范为不同的实现策略留出了空间,甚至在一个实现中,它将取决于如何使用类。此外,您的问题更多的是“什么时候可以失败”,这取决于它可能失败的原因,例如:一个类可以在一个时间点加载以验证它的存在,但是在它实际首次使用时稍后初始化。在这两个时间点,操作可能会失败,从而导致NoClassDefFoundError

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

  • 必须在链接时解析的类(例如超类)
  • 必须在验证时加载的类
  • 可以将其加载延迟到第一次实际使用的类

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

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

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

Oracle的JVM更喜欢方法的第一次使用,读取:在方法入口处验证它,因此,将调用移动到另一个不会执行的方法,无论是否在另一个类中,在您的情况下就足够了。 / p>

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