4年前 (2020-09-26)  爪哇岛 |   抢沙发  710 
文章评分 0 次,平均分 0.0

Java8永久代被移除

OutOfMemoryError,这是由于您的HotSpotVM的PermGen空间耗尽造成的。这个问题很常见,通常是由于应用程序的动态重新部署(例如,从应用程序服务器加载和卸载Java EE应用程序)通常会触发类元数据泄漏;最终导致固定PermGen空间完全耗尽。

然而,oraclejrockit和ibmjre一开始并没有使用PermGen空间。它们使用C堆(本机内存)来存储类元数据。

当前的oraclejvm策略是将HotSpot和JRockit产品线合并到一个JVM项目中,该项目将包含每个VM的最佳特性。

没有永久代的未来对你意味着什么?

没有PermGen空间意味着不再通过–XX:PermSize&-XX:MaxPermSize配置和调整此内存空间,因为类元数据将移动到本机内存(C-Heap)和OldGen空间,不再可配置。

但是,我建议您提高对本机内存空间(例如C-Heap)的警觉性和监视级别。与Java堆一样,您可能仍然面临本机内存空间的容量或泄漏问题,因此请确保在JVM容量规划练习中包含此分析。

永久空间移除了吗?

还不完全。正如在Oracle博客中提到的,PermGen的删除需要一段时间,所以在java7的更多版本发布之前不要期望完全删除。

最后,我使用Java 7示例程序运行了一个简单的测试详细:gc已打开如您所见,PermGen内存空间仍然存在于这个热点版本(构建21.0-b17)中。

Java从永久代PermGen到MetaSpace元空间的迁移

Java从永久代PermGen到MetaSpace元空间的迁移

Java8中的metaspace元空间

在这篇文章中,我们将看到一个JVM更新,即删除永久代生成。在这里,我们将看到为什么需要删除永久代和它的替代元空间

这就是Java6中堆结构的样子

Java从永久代PermGen到MetaSpace元空间的迁移

永久代Permgen

包含虚拟机本身的所有反射数据的池,例如类和方法对象。对于使用类数据共享的javavm,这一代分为只读和读写两部分。

永久生成包含JVM描述应用程序中使用的类和方法所需的元数据。永久生成由JVM在运行时根据应用程序使用的类填充。此外,javase库类和方法可以存储在这里。

如果JVM发现不再需要类,并且其他类可能需要空间,则可以收集(卸载)这些类。永久生成包含在完整的垃圾收集中

  • 用于JVM类元数据的Java堆区域。
  • Hotspot对Java类的内部表示。
  • 类层次结构信息、字段、名称
  • 方法编译信息和字节码
  • 变量
  • 常数池与符号分解

PermGen永久代大小

限制为MaxPermSize–默认值为64M-85M

与Java堆相邻:使用非连续堆-card table(一种记录一代中oop发生变化的记忆集)来识别旧gen和permgen的年轻引用将更加昂贵和复杂。

一旦耗尽抛出内存错误“永久空间”。

  • 应用程序可以清除导致类卸载的引用。
  • 使用更大的MaxPermSize重新启动。

所需的大小取决于类的数量、方法的大小、常量池的大小。

为什么PermGen被淘汰了?

  • 启动时大小固定-难以调整。
  • -XX: MaxPermSize=?
  • 内部热点类型是Java对象:可以用完整的GC移动,不透明,不强类型,很难调试,需要元数据。
  • 简化完整集合:每个收集器的元数据的特殊迭代器
  • 想同时释放类数据,而不是在GC暂停期间
  • 实现PermGen限制的未来改进。

JVM元数据现在去了哪里?

Java从永久代PermGen到MetaSpace元空间的迁移

metaspace元空间

永久生成(PermGen)空间已经被完全移除,并被一个称为元空间的新空间所取代。

删除PermGen的结果是,显然PermSize和MaxPermSize JVM参数被忽略,您永远不会得到java.lang.OutOfMemoryError:PermGen错误。

jdk8热点JVM现在使用本机内存来表示类元数据,称为Metaspace

  • 利用Java语言规范属性:类和关联的元数据生存期与类装入器的生存期匹配。
  • 每个装载机存储区域–元空间
  • 仅线性分配
  • 不单独回收(除了重定义类和类加载失败)
  • 无GC扫描或压缩
  • 无需重新定位元空间对象
  • 类加载器被GC发现死机时集体回收

java8metaspace内存溢出的排查思路可参考这篇文章:https://javakk.com/160.html

元空间内存分配模型

  • 类元数据的大多数分配现在都是从本机内存中分配的。
  • 用于描述类元数据的类已被删除。
  • 为元数据分配了多个映射的虚拟内存空间。
  • 按类装入器块列表分配
  • 块大小取决于类装入器的类型。
  • sun/reflect/Delegating类加载器的较小块。
  • 将区块返回到空闲区块列表。
  • 清空时返回虚拟内存空间。
  • 减少分裂的策略。

我们将看到如何为元数据分配虚拟内存空间,以及它如何使用此图片加载每类加载程序

Java从永久代PermGen到MetaSpace元空间的迁移

您可以看到如何分配虚拟内存空间(vs1、vs2、vs3)以及如何分配每个类装入器块。CL-类加载器

理解“_mark标记”和“_klass指针”

要理解下一个图表,您需要了解这些指针。

在JVM中,每个对象都有一个指向其类的指针,但只指向其具体类,而不指向其接口或抽象类。

对于32位JVM:

_标记:4字节常量

_klass:指向类的4字节指针

内存中对象布局中的第二个字段(对于32位JVM,偏移量为4,对于64位JVM,偏移量为8,从内存中对象的地址开始偏移量为8)指向内存中对象的类定义。

对于64位JVM:

_标记:8字节常量

_klass:指向类的8字节指针

对于具有压缩OOP的64位JVM:

_标记:8字节常量

_klass:指向类的4字节指针

Java对象内存布局

Java从永久代PermGen到MetaSpace元空间的迁移

带有压缩指针的Java对象内存布局

Java从永久代PermGen到MetaSpace元空间的迁移

压缩指针摘要

  • 64位平台的默认值。
  • 压缩对象指针-XX:+UseCompressedOops
  • “oops”是“普通”对象指针。
  • 对象指针在Java堆中的对象中被压缩到32位。
  • 使用堆基(如果Java堆在较低的26G内存中,则为零)。
  • 压缩类指针-XX:+UseCompressedClassPointers
  • 对象有一个指向VM元数据类(第二个字)的指针,该类压缩为32位。
  • 使用压缩类指针空间的基。

元空间与压缩类指针空间的区别

  • 压缩的类指针空间只包含类元数据。
  • InstanceKlass,arraylass
  • 仅当UseCompressedClassPointers为true时。
  • 出于性能考虑,其中包括Java虚拟表。
  • 我们仍在缩小此元数据类型。
  • Metaspace包含所有其他可能很大的类元数据。
  • 方法,字节码,ConstantPool。。。

元空间调整

可以使用-XX:MaxMetaspaceSize标志设置最大元空间大小,默认值为无限,这意味着只有系统内存是限制。-XX:MetaspaceSize调优标志定义了元空间的初始大小,如果不指定此标志,则元空间将根据运行时的应用程序需求动态重新调整大小。

调整标志-MaxMetaspaceSize

  • -XX: MaxMetaspaceSize={unmited}
  • 元空间受计算机上内存量的限制。
  • 在发生过度交换和本地分配失败之前,限制类元数据使用的内存。
  • 如果怀疑类装入器内存泄漏,请使用。
  • 如果地址空间可能耗尽,请在32位上使用。
  • 初始MetaspaceSize 21 mb–GC初始高水位标记,用于完成完整的GC收集类。
  • GC的目的是检测死区类装入器和卸载类。
  • 如果启动时执行太多GC,则设置为更高的限制。
  • 可能使用PermSize设置的相同值来延迟初始GC。
  • 高水痕随着后续收集的增加而增加,在下一次元空间GC之前,合理数量的主室。
  • 请参见MinMetaspaceFreeRatioMaxMetaspaceFreeRatio
  • 与类似GC自由比参数类似的解释

调整标志-CompressedClassSpaceSize

  • 仅当-XX:+UseCompressedClassPointers(默认值为64位)时有效。
  • -XX:CompressedClassSpaceSize=1G。
  • 由于这个空间目前在启动时是固定的,所以一开始就要保留大量空间。
  • 使用前不提交。
  • 未来的工作是使这片空间变得可扩展。
  • 不需要是连续的,只能从基地址访问。
  • 而是将更多的类元数据转移到元空间。
  • 将来可能会根据PredictedLoadedClassCount(现在是实验标志)进行人体工程学设置。
  • 设置其他内部JVM数据结构的大小,如加载类的字典。

元空间工具

  • jmap-permstat选项重命名为jmap-clstats
  • 打印Java堆的类装入器统计信息。对于每个类装入器,将打印其名称、活动性、地址、父类装入器以及它已加载的类的数量和大小。此外,还打印了内插字符串的数量和大小。
  • jstat-gc选项显示Metaspace而不是PermGen。
  • jcmd<pid>GC.class_stats统计.
  • 提供类元数据大小的详细柱状图。
  • 使用-XX:+UnlockDiagnosticVMOptions启动java

改进的GC性能

如果您很好地理解了元空间的概念,就很容易看到垃圾收集方面的改进

  • 在完全收集期间,不扫描指向元数据指针的元数据。
  • 删除了许多用于元数据扫描的复杂代码(尤其是CMS)。
  • Metaspace包含一些指向Java堆的指针。
  • 指向类元数据中java/lang/Class实例的指针
  • 指向数组类元数据中组件java/lang/Class的指针
  • 没有压缩元数据的成本。
  • 减少根扫描(不扫描已加载类的VM字典和其他内部哈希表)。
  • 缩短完整收集时间。
  • 并行标记循环后G1中的类卸载工作
 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/476.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册