首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

Eclipse OpenJ9 中的类共享(2)工作原理

Eclipse OpenJ9 中的类共享(2)工作原理

工作原理让我们先看看关于共享类特性如何操作的技术细节。
启用类共享要启用类共享,请将 -Xshareclasses[:name=<cachename>] 添加到现有的                                Java 命令行中。JVM                                启动时,它会查找给定名称的共享缓存(如果未提供名称,则使用当前用户名),并根据需要连接现有共享缓存或创建一个新缓存。
可以使用参数 -Xscmx<size>[k|m|g                                指定共享缓存的大小。此参数仅在创建新共享缓存时适用。如果省略此选项,则会使用与平台相关的默认值。注意,一些操作系统设置会限制可分配的共享内存大小。例如,Linux                                上的 SHMMAX 通常设置为大约 32MB。要了解这些设置的更多细节,请参阅相关 的共享类部分。
共享类缓存共享类缓存是一个固定大小的共享内存,其在 JVM                                的生命周期结束后或系统重新启动后仍然存在,除非使用的是非持久性共享缓存。一个系统上可以有任意数量的共享缓存,而且所有缓存都需要遵从操作系统的设置和限制。
没有 JVM 拥有共享缓存,也没有主/从 JVM 的概念。实际上,任意数量的 JVM 可以同时对共享缓存进行读写。
共享缓存的大小无法增加。当它被填满时,JVM                                仍能从中加载类,但无法再向其中存储任何数据。您可以预先创建一个较大的共享类缓存,同时为可使用的共享缓存空间设置一个软性上限。如果想将更多数据存储到共享缓存中且不关闭与它相连的                                JVM,可以调高此上限。请查阅  ,以获得关于软性上限的更多细节。
此外,一些 JVM 实用程序也可以管理活跃缓存。我们将在下面的 部分讨论这些实用程序。
当使用 JVM 命令行显式销毁共享缓存时,共享缓存将被删除。
类是如何被缓存的?当 JVM                                加载一个类时,它首先会在类加载器缓存中查看它需要的类是否已经存在。如果已经存在,那么它会从类加载器缓存返回这个类。否则,它会从文件系统加载这个类,并将其作为                                        defineClass() 调用的一部分写入缓存中。因此,非共享 JVM                                具有以下类加载器查找顺序:
  • 类加载器缓存
  • 父类加载器
  • 文件系统
相较而言,具有类共享特性的 JVM 采用了以下顺序:
  • 类加载器缓存
  • 父类加载器
  • 共享类缓存
  • 文件系统
可以使用公共 Helper API 在共享类缓存中读写类。Helper API 已集成到                                        java.net.URLClassLoader 中(在 Java 9 及更高版本中,还集成到了                                        jdk.internal.loader.BuiltinClassLoader                                中)。因此,任何扩展 java.net.URLClassLoader                                的类加载器都可以免费获得类共享支持。对于自定义类加载器,OpenJ9 提供了 Helper                                API,以便可以在自定义类加载器上实现类共享。
可以缓存哪些内容?共享类缓存可以包含引导程序和应用程序类、描述这些类的元数据,以及提前(AOT)编译代码。
在 OpenJ9 实现中,Java 类分为两个部分:
  • 一个称为 ROMClass 的只读部分,包含该类的所有不可变数据
  • 一个 RAMClass 部分,包含可变数据,比如静态类变量
RAMClass 指向它在 ROMClass 中的数据,但这两部分是完全分开的。因此,在                                JVM 之间以及同一 JVM 中的 RAMClass 之间共享 ROMClass                                是非常安全的。
在非共享情况下,当 JVM 加载一个类时,它会单独创建 ROMClass 和                                RAMClass,并将它们都存储在其本地进程内存中。在共享情况下,如果 JVM 在共享类缓存中找到一个                                        ROMClass,那么它只需在其本地内存中创建                                RAMClass;然后该 RAMClass 就会引用共享的 ROMClass。
由于大部分类数据都存储在 ROMClass 中,所以可以节省内存(参阅 部分中更详细的讨论)。通过填充的缓存,JVM                                启动时间也得到了明显改进,因为定义每个缓存类的一些工作已经完成,而且类是从内存加载而不是从文件系统加载的。填充新共享缓存的启动时间开销并不显著,因为每个类只需在定义时重新定位到共享缓存中。
AOT 编译代码也存储在共享缓存中。启用共享类缓存时,AOT 编译器会自动激活。AOT 编译允许将 Java                                类编译为原生代码,供同一程序的后续执行使用。AOT 编译器在应用程序运行过程中动态生成原生代码,并将所有已生成的 AOT                                代码缓存到共享类缓存中。AOT 编译代码的执行速度通常比解释字节码快,但没有 JIT 编译代码快。执行该方法的后续 JVM                                可以加载并使用来自共享缓存的 AOT 代码,而不会出现生成 JIT                                编译代码时遇到的性能下降,从而实现了更快的启动时间。创建新的共享缓存时,可以使用                                        -Xscminaot<x> 和                                        -Xscmaxaot<x> 选项来设置共享缓存中的 AOT                                空间的大小。如果既不使用 -Xscminaot 也不使用                                -Xscmaxaot,那么只要有可用的空闲空间,AOT 代码就会存储到共享缓存中。
如果文件系统上的某个类发生更改,会发生什么?由于共享类缓存可以无限期持久存在,所以可能会发生文件系统更新,使得共享缓存中的类和 AOT                                代码失效。当类加载器请求一个共享类时,返回的类应该始终与从文件系统加载的类相同。在加载类时,这一检查会透明地进行,所以用户可以在共享类缓存的生命周期中修改和更新任意数量的类,因为知道系统总会加载正确的类。
类更改中的缺陷:两个示例假设 JVM 将一个类 C1 存储到共享缓存中。然后,在 JVM 关闭时,C1 被更改并重新编译。当 JVM 重新启动时,它不应加载 C1                                的缓存版本。
同样,假设 JVM 的类路径是 /mystuff:/mystuff/myClasses.jar。它从                                        myClasses.jar 将 C2 加载到共享缓存中。然后,将一个不同的 C2.class                                添加到 /myStuff,并启动另一个 JVM 来运行同一个应用程序。JVM 也不应该加载                                        C2 的缓存版本。
JVM 检测文件系统更新的方法是,将时间戳值存储到共享缓存中,并在每次加载类时将该缓存值与实际值进行比较。如果它检测到某个 JAR                                文件已更新,并且不知道哪些类发生了更改,那么缓存中来自该 JAR 的所有类和 AOT                                代码都会立刻被标记为过时,那么无法再从该缓存中加载它们。当从文件系统加载来自该 JAR                                的类并重新添加到缓存中时,只有已更改的类才会得到完整添加;未更改的类实际上被设置为未过时。
无法从共享类缓存中清除类,但 JVM 会尝试最有效地利用它所拥有的空间。例如,同一个类绝不会被添加两次,即使是从许多不同的位置加载它。因此,如果                                3 个不同的 JVM 分别从 /A.jar、/B.jar 和 /C.jar 加载同一个类 C3,该类的数据仅会添加一次,但是有 3                                段元数据来描述加载这个类的 3 个位置。
返回列表