原生镜像中的类初始化

Java 语义要求在运行时首次访问类时初始化该类。对于以下两个原因,类初始化对提前编译 Java 应用程序以生成原生可执行文件具有负面影响

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

为了减少类初始化的负面影响,原生镜像支持在构建时进行类初始化:它可以在构建可执行文件时初始化类,从而使运行时初始化和检查变得不必要。所有来自在构建时初始化的类的静态状态都存储在可执行文件中。访问在构建时初始化的类的静态字段对应用程序是透明的,效果等同于在运行时初始化该类。

但是,Java 类初始化语义会施加一些限制,使类初始化策略变得复杂,例如

  • 当初始化一个类时,它所有具有默认方法的超类和超接口也必须初始化。但是,没有默认方法的接口不会初始化。为了适应这一要求,使用了短期“相关超类型”,以及用于具有默认方法的类和接口的子类型的“相关子类型”。
  • 在构建时初始化的类型的相关超类型也必须在构建时初始化。
  • 在运行时初始化的类型的相关子类型也必须在运行时初始化。
  • 可执行文件中不得存在在运行时初始化的类的任何实例。

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

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

构建时初始化 #

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

自动初始化安全类 #

对于应用程序类,原生镜像会尝试找到可以在构建时安全初始化的类。如果类的所有相关超类型都是安全的,并且类初始化器不调用任何不安全的函数或初始化其他不安全的类,则该类被视为安全的。

如果满足以下条件,则函数被视为不安全:

  • 它会传递地调用原生代码(例如 System.out.println):不会分析原生代码,因此原生镜像无法知道是否执行了非法操作。
  • 它调用无法简化为单个目标的函数(虚函数)。此限制避免了对静态初始化器的安全性分析的搜索空间爆炸。
  • 它被原生镜像替换。运行被替换函数的初始化器将导致主机 Java 虚拟机 (JVM) 中的结果与生成的执行文件中不同。因此,安全性分析会将某些函数视为安全的,但调用它们会导致非法状态。

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

注意:您还可以 显式指定类初始化

联系我们