Native Image 中的类初始化

Java 的语义要求类在运行时首次被访问时才进行初始化。类初始化对提前编译 Java 应用程序会产生负面影响,原因有二:

  • 它会显著降低原生可执行文件的性能:每次访问类(通过字段或方法)都需要进行检查以确保该类已初始化。如果没有优化,这可能会使性能降低一半以上。
  • 它增加了应用程序启动所需的计算量和时间。例如,简单的“Hello, World!”应用程序就需要初始化 300 多个类。

为了减少类初始化的负面影响,Native Image 支持构建时类初始化:它可以在构建可执行文件时初始化类,从而省去运行时的初始化和检查。所有已初始化类的静态状态都存储在可执行文件中。对在构建时初始化的类静态字段的访问对应用程序是透明的,其行为就像该类是在运行时初始化的一样。

然而,Java 类初始化语义施加了一些约束,使类初始化策略变得复杂,例如:

  • 当一个类被初始化时,其所有超类和带有默认方法的超接口也必须被初始化。然而,不带默认方法的接口则不被初始化。为满足此要求,使用了短期“相关超类型”,以及带有默认方法的类和接口的子类型的“相关子类型”。
  • 在构建时初始化的类型的相关超类型也必须在构建时初始化。
  • 在运行时初始化的类型的相关子类型也必须在运行时初始化。
  • 在运行时初始化的类实例不得出现在可执行文件中。

为了享受 Native Image 的完整开箱即用体验并仍获得构建时初始化的好处,Native Image 会执行两项操作:

要跟踪哪些类已初始化以及原因,请将命令行选项 -H:+PrintClassInitialization 传递给 native-image 工具。此选项可帮助您配置 native image 构建器以按要求工作。目标是在构建时初始化尽可能多的类,同时保持应用程序的正确语义。

构建时初始化 #

Native Image 在构建时初始化大多数 JDK 类,包括垃圾收集器、重要的 JDK 类和反优化器。对于所有在构建时初始化的类,Native Image 提供了适当的支持,以确保即使类初始化发生在构建时,其语义也能保持一致。如果您发现由于构建时类初始化导致 JDK 类行为不正确的问题,请报告问题

安全类的自动初始化 #

对于应用程序类,Native Image 尝试查找可以在构建时安全初始化的类。如果一个类的所有相关超类型都是安全的,并且该类的初始化程序不调用任何不安全的方法或初始化其他不安全的类,则该类被认为是安全的。

如果一个方法符合以下条件,则被认为是不安全的:

  • 它间接调用原生代码(例如 System.out.println):原生代码未被分析,因此 Native Image 无法知道是否执行了非法操作。
  • 它调用一个无法归结为单个目标的方法(一个虚方法)。此限制避免了静态初始化器安全分析的搜索空间爆炸。
  • 它被 Native Image 替换。运行被替换方法的初始化器将在托管 Java 虚拟机(JVM)中产生与生成的执行文件不同的结果。因此,安全分析会认为某些方法是安全的,但调用它们会导致非法状态。

所有被证明安全的类列表将通过将 -H:+PrintClassInitialization 命令行选项传递给 native-image 工具输出到文件。

注意:您也可以明确指定类初始化

联系我们