跳到主要内容

JEP 122:删除永久代

概括

从 Hotspot JVM 中删除永久代,因此需要调整永久代的大小。

非目标

将类数据共享扩展到应用程序类。减少类元数据所需的内存。启用类元数据的异步收集。

成功指标

类元数据、内部字符串和类静态变量将从永久代移至 Java 堆或本机内存。

Hotspot JVM 中永久代的代码将被删除。

根据一组尚未选择的基准测试,应用程序启动和占用空间不会下降超过 1%。

动机

这是 JRockit 和 Hotspot 融合工作的一部分。 JRockit 客户不需要配置永久代(因为 JRockit 没有永久代)并且习惯不配置永久代。

描述

将 Hotspot 中永久代的部分内容移至 Java 堆,其余部分移至本机内存。

Hotspot 的 Java 类表示(此处称为类元数据)当前存储在 Java 堆的一部分(称为永久代)中。此外,interned Strings 和类静态变量存储在永久代中。永久代由 Hotspot 管理,并且必须有足够的空间来容纳 Java 应用程序使用的所有类元数据、内部字符串和类静态。类元数据和静态变量在加载类时在永久代中分配,并在卸载类时从永久代中进行垃圾收集。当永久代被 GC 时,内部字符串也会被垃圾回收。

建议的实现将在本机内存中分配类元数据,并将内部字符串和类静态数据移动到 Java 堆。 Hotspot 将为类元数据显式分配和释放本机内存。新类元数据的分配将受到可用本机内存量的限制,而不是由 -XX:MaxPermSize 的值固定,无论是默认值还是在命令行上指定。

类元数据的本机内存分配将在大小足以容纳多个类元数据的块中完成。每个块将与一个类加载器关联,并且该类加载器加载的所有类元数据将由 Hotspot 从该类加载器的块中分配。将根据需要为类加载器分配额外的块。块大小将根据应用程序的行为而变化。将选择大小以限制内部和外部碎片。当类加载器终止时,将通过释放与类加载器关联的所有块来释放类元数据的空间。类元数据在类的生命周期内不会被移动。

备择方案

消除调整永久代大小的需要的目标可以通过拥有可以增长的永久代来实现。还有一些额外的数据结构必须随着永久代的增长而增长(例如卡表和块偏移表)。为了有效实现,永久代需要看起来像一个连续的空间,其中一些部分不可用。

测试

在测试期间需要监视本机内存使用的变化以查找内存泄漏。

风险和假设

Hotspot JVM 的更改范围是主要风险。此外,准确确定需要更改的内容可能只能在实施过程中确定。

这是一个对所有垃圾收集器都有广泛影响的大型项目。有关永久代及其工作原理的知识存在于热点 JVM 的运行时和编译器部分。垃圾收集器外部的数据结构将被更改,以方便垃圾收集器处理本机内存中的类元数据。

作为该项目的一部分,JVM 的某些部分可能需要重新实现。例如,类数据共享将受到影响,并且可能需要全部或部分重新实现。

类的重新定义是一个存在风险的领域。重定义依赖于永久代收集期间类元数据的垃圾收集(即,重定义当前不释放已重新定义的类,因此需要某种方法来发现已重新定义的类的元数据何时可以被释放)。

将 interned Strings 和类静态数据移至 Java 堆可能会导致内存不足异常或 GC 次数增加。用户可能需要对 -Xmx 进行一些调整。

使用 UseCompressedOops 选项,可以使用与 Java 堆中的指针相同的方式压缩指向类元数据(在永久代中)的指针。这会带来显着的性能提升(大约几个百分点)。指向本机内存中元数据的指针将以类似的方式压缩,但实现不同。后一种实现的性能可能不如将指针压缩到 Java 堆中那么高。压缩元数据的指针的要求可能对元数据的大小设置上限。例如,如果实现要求将所有元数据分配到某个地址下方(例如低于 4g 限制),这将限制元数据的大小。

依赖关系

需要重新实现了解永久代的工具。可维护性代理、jconsole、Java VisualVM 和 jhat 是受影响的工具示例。

影响

  • 其他 JDK 组件:了解永久代的工具。

  • 兼容性:与永久代相关的命令行标志将变得过时。

  • 文档:需要删除对永久代的引用。