Java8:从永久代PermGen到元空间Metaspace

众所周知java8的新特性之一是完全删除了永久生成(PermGen)空间,自jdk7发布以来,Oracle就已经宣布了这一点。例如,自jdk7以来,内部字符串已经从PermGen空间中删除。jdk8版本完成了它的退役。本文将与大家分享到目前为止我们在PermGen继任者:Metaspace上发现的信息。我们还将比较hotspots1.7和hotspots1.8(b75)在执行Java程序“泄漏”类元数据对象时的运行时行为。一旦java8正式发布,围绕Metaspace的最终规范、调优标志和文档应该可以使用。

元空间Metaspace

一个新的记忆空间诞生了

jdk8热点JVM现在使用本机内存来表示类元数据,称为Metaspace;类似于oraclejrockitIBMJVM java.lang.OutOfMemoryError:PermGen空间问题,无需再调整和监视此内存空间…不要太快。虽然这个更改在默认情况下是不可见的,但是接下来我们将向您展示您仍然需要担心类元数据内存占用。请记住,这个新特性并不能神奇地消除类和类加载器内存泄漏。您将需要使用不同的方法并通过学习新的命名约定来跟踪这些问题。

总而言之:

PermGen空间情况

此内存空间已完全删除。

PermSizeMaxPermSize JVM参数将被忽略,如果在启动时出现,则会发出警告。

元空间内存分配模型

类元数据的大多数分配现在都是从本机内存中分配的。

用于描述类元数据的klasse已被删除。

元空间容量

默认情况下,类元数据分配受可用本机内存量的限制(容量当然取决于是否使用32位JVM而不是64位以及操作系统虚拟内存可用性)。

有一个新的标志可用(MaxMetaspaceSize),允许您限制用于类元数据的本机内存量。如果不指定此标志,元空间将根据运行时的应用程序需求动态调整大小。

元空间垃圾回收

一旦类元数据使用量达到“MaxMetaspaceSize”,就会触发对死类和类加载器的垃圾回收。

显然需要对元空间进行适当的监视和调优,以限制此类垃圾收集的频率或延迟。过多的元空间垃圾收集可能是类、类加载器内存泄漏或应用程序大小不足的症状。

Java堆空间影响

一些杂项数据已移动到Java堆空间。这意味着您可能会在未来的jdk8升级后观察到Java堆空间的增加。

元空间监测

Metaspace用法可从HotSpot 1.8详细GC日志输出中获得。

根据我们对b75的测试,JstatJVisualVM还没有更新,旧的PermGen空间引用仍然存在。

理论足够了,让我们看看这个新的内存空间是如何通过我们泄漏的Java程序运行的…

更多细节可以参考这一篇介绍:http://javakk.com/417.html

PermGen与Metaspace运行时比较

为了更好地理解新元空间内存空间的运行时行为,我们创建了一个类元数据泄漏Java程序。你可以在这里下载源代码。

将测试以下场景:

  • 使用jdk1.7运行Java程序,以监视并耗尽设置为128mb的PermGen内存空间。
  • 使用jdk1.8(b75)运行Java程序,以监视新Metaspace内存空间的动态增加和垃圾收集。
  • 使用jdk1.8(b75)运行Java程序,通过将MaxMetaspaceSize值设置为128mb来模拟元空间的消耗

JDK 1.7@64位-永久代消耗

  • 具有50K配置迭代的Java程序
  • 1024 MB的Java堆空间
  • Java PermGen空间为128 MB(-XX:MaxPermSize=128m
    www.javakk.com

正如您在JVisualVM中看到的,PermGen耗尽是在加载了大约30K+个类之后达到的。我们也可以从程序和GC输出中看到这种消耗。

Class metadata leak simulator

Author: Pierre-Hugues Charbonneau

http://javaeesupportpatterns.blogspot.com

ERROR: java.lang.OutOfMemoryError: PermGen space

在这里插入图片描述

现在让我们使用hotspotsjdk1.8jre执行该程序。

JDK 1.8@64位–元空间动态调整大小

  1. 具有50K配置迭代的Java程序
  2. 1024 MB的Java堆空间
  3. Java元空间:无边界(默认)
在这里插入图片描述

从详细的GC输出中可以看到,JVM元空间确实从20mb动态扩展到328mb的保留本机内存,以满足Java程序增加的类元数据内存占用。我们还可以观察到JVM试图销毁任何死类或类加载器对象时的垃圾收集事件。由于我们的Java程序正在泄漏,JVM别无选择,只能动态扩展元空间内存空间。该程序能够在没有OOM事件的情况下运行其50K次迭代,并加载了50K+个类。让我们转到最后一个测试场景。

JDK 1.8@64位-元空间耗尽

  1. 具有50K配置迭代的Java程序
  2. 1024 MB的Java堆空间
  3. Java元空间:128 MB(-XX:MaxMetaspaceSize=128m
    在这里插入图片描述

正如您在JVisualVM中看到的,元空间耗尽是在加载了大约30K+个类之后达到的;这与jdk1.7的运行非常相似。我们也可以从程序和GC输出中看到这一点。另一个有趣的观察是,保留的本机内存占用是指定的最大大小的两倍。这可能表明有机会微调Metaspace resize策略(如果可能),以避免本机内存浪费。

现在在下面找到我们从Java程序输出中得到的异常。

Class metadata leak simulator

Author: Pierre-Hugues Charbonneau

http://javaeesupportpatterns.blogspot.com

ERROR: java.lang.OutOfMemoryError: Metadata space

正如预期的那样,将元空间限制在128MB,就像我们在JDK1.7的基线运行中所做的那样,不允许我们完成程序的50K迭代。JVM抛出了一个新的OOM错误。上面的OOM事件是JVM在内存分配失败后从元空间抛出的


在这里插入图片描述

当前的观察结果明确表明,为了避免诸如上一个测试场景触发的过多的元空间GC或OOM条件等问题,需要进行适当的监控和调优。以后的文章可能会包括性能比较,以确定与此新特性相关的潜在性能改进。

文章来源:http://javakk.com/421.html
也欢迎大家关注我的公众号【Java老K】获取更多干货

你可能感兴趣的:(Java8:从永久代PermGen到元空间Metaspace)