正如大家所知,JDK 8 Early Access版已经提供下载。这使开发者可以体验Java8的新特性。其中之一,是Oracle从JDK7发布以来就一直宣称的要完全移除永久代空间。例如,字符串内部池,已经在JDK7中从永久代中移除。JDK8的发布将宣告它的终结。这篇文章将会分享到目前为止对 PermGen 继任者:Metaspace的了解。我们将通过运行一个存在类元数据对象“泄漏”的程序,来对比HotSpot1.7与HotSpot1.8(b75,译者注:翻译文章时已经到b118)的运行时行为。待Java 8 正式发布之后,才会提供最终的规范,优化参数和相关文档。
JDK8 HotSpot JVM 使用本地内存来存储类元数据信息并称之为:元空间(Metaspace);这与Oracle JRockit 和IBM JVM’s很相似。这将是一个好消息:意味着不会再有java.lang.OutOfMemoryError: PermGen问题,也不再需要你进行调优及监控内存空间的使用……但请等等,这么说还为时过早。在默认情况下,这些改变是透明的,接下来我们的展示将使你知道仍然要关注类元数据内存的占用。请一定要牢记,这个新特性也不能神奇地消除类和类加载器导致的内存泄漏。你需求使用不同的方法以及遵守新的命名约定来追踪这些问题。我推荐大家阅读有关PermGen移除总结和Jon对此的评论。
总结如下:
前面已经从理论上充分说明,下面让我们通过“泄漏”程序进行新内存空间的观察……
为了更好地理解Metaspace内存空间的运行时行为,我们建立了一个类元数据泄漏程序。可以从此处下载源代码。
将进行以下几种场景的测试:
可以从上面的JVisualVM的截图看出:当加载超过3万个类之后,PermGen被耗尽。我们也能通过程序和GC的输出观察耗尽的过程。
Class metadata leak simulator Author: Pierre-Hugues Charbonneau http://javaeesupportpatterns.blogspot.com ERROR: java.lang.OutOfMemoryError: PermGen space
下面我们使用HotSpot JDK 1.8 JRE来执行程序。
从上面的截图可以看到详细的GC输出日志,JVM Metaspace进行了动态扩展,本地内存的使用由20MB增长到328MB,以满足程序中不断增长的类数据内存占用需求。我们也能观察到JVM的垃圾回收事件—试图销毁僵死的类或类加载器对象。但是,由于我们程序的泄漏,JVM别无选择只能动态扩展Metaspace内存空间。程序能够运行5万次迭代,加载超过5万个类,而没有出现OOM事件。下面继续进行最后的一个测试场景:
可以从上面的JVisualVM的截图看出:当加载超过3万个类之后,Metaspace被耗尽;与JDK1.7运行时非常相似。我们也能通过程序和GC的输出观察耗尽的过程。另一个有趣的现象是,保留的原生内存占用量是设定的最大大小两倍之多。这可能表明,如果可能的话,可微调元空间容量大小策略,来避免本地内存的浪费。
从Java程序的输出中看到如下异常。
Class metadata leak simulator Author: Pierre-Hugues Charbonneau http://javaeesupportpatterns.blogspot.com ERROR: java.lang.OutOfMemoryError: Metadata space完成!
正如预期的那样,像运行JDK1.7基线时一样,限定128 MB大小元空间时,并不能让程序完成50万次迭代。一种新的OOM错误被JVM抛出。上述OOM事件,是由于元空间内存分配失败由JVM抛出的。
我希望您能喜欢这篇较早的对Java8元空间的分析和实验文章。目前的观察有力地表明,适当的监控和调优是必须的,以此来避免诸如,过度的在元空间中的GC,或像我们测试场景下触发OOM的条件。后面的文章中可能包括,性能上的比较,以确定这一新特性是否有潜在的性能上的提升。
参考:Java 8: From PermGen to Metaspace from ourJCG partner Pierre-Hugues Charbonneau at theJava EE Support Patterns & Java Tutorial blog.
原文地址