Java 8: 从永久代到原空间

原文链接:https://www.javacodegeeks.com/2013/02/java-8-from-permgen-to-metaspace.html

        您可能已经注意到,JDK 8 早期访问版本现在可以下载了。这意味着Java开发人员可以开始使用Java 8的一些新的语言和运行时特性进行试验。其中一个特性是完全删除了永久代(PermGen),这是自JDK 7发布以来Oracle宣布的。例如,从JDK 7开始,字符串内部池已经从PermGen空间中删除,JDK 8发行版宣告了它的退役。本文将分享到关于PermGen最新继任者--Metaspace的信息。我们还将比较HotSpot 1.7和HotSpot 1.8 (b75)在执行Java程序“泄漏”类元数据对象时的运行时行为。关于Metaspace的最终规范、调优标志和文档会在Java 8正式发布后可以获得。

        As you may be aware, the JDK 8 Early Access is now available for download. This allows Java developers to experiment with some of the new language and runtime features of Java 8. One of these features is the complete removal of the Permanent Generation (PermGen) space which has been announced by Oracle since the release of JDK 7. Interned strings, for example, have already been removed from the PermGen space since JDK 7. The JDK 8 release finalizes its decommissioning. This article will share the information that we found so far on the PermGen successor: Metaspace. We will also compare the runtime behavior of the HotSpot 1.7 vs. HotSpot 1.8 (b75) when executing a Java program “leaking” class metadata objects. The final specifications, tuning flags and documentation around Metaspace should be available once Java 8 is officially released.

原空间:

一个新的记忆空间诞生

        JDK 8热点JVM现在使用本地内存代表类元数据,称为Metaspace; 类似于Oracle JRockit和IBM JVM。好消息是,它意味着不会再有 java.lang.OutOfMemoryError: PermGen 永久代空间问题,不需要再调优和监视这个内存空间…虽然这个更改在默认情况下是不可见的,但是接下来我们将向您展示,您仍然需要担心类元数据内存占用。请记住,这个新特性并不能神奇地消除类和类加载器的内存泄漏问题。您将需要使用不同的方法并遵守新的命名规约来跟踪这些问题。我建议您阅读PermGen移除总结和Jon对此主题的评论

Metaspace:

A new memory space is born

The JDK 8 HotSpot JVM is now using native memory for the representation of class metadata and is called Metaspace;  similar to the Oracle JRockit and IBM JVM’s. The good news is that it means no more java.lang.OutOfMemoryError: PermGen space problems and no need for you to tune and monitor this memory space anymore…not so fast. While this change is invisible by default, we will show you next that you will still need to worry about the class metadata memory footprint. Please also keep in mind that this new feature does not magically eliminate class and classloader memory leaks. You will need to track down these problems using a different approach and by learning the new naming convention. I recommend that you read the PermGen removal summary and comments from Jon on this subject.

总之:

PermGen 空间情况

 

  • 这个内存空间被完全删除。
  • PermSize和MaxPermSize JVM参数已被忽略,如果在启动时出现,则会发出警告。

元空间内存分配模型

  • 大部分类元数据都是从本地内存中分配。
  • 用于描述类元数据的“klasses”已被删除。

In summary:

PermGen space situation

  • This memory space is completely removed.
  • The PermSize and MaxPermSize JVM arguments are ignored and a warning is issued if present at start-up.

Metaspace memory allocation model

  • Most allocations for the class metadata are now allocated out of native memory.
  • The klasses that were used to describe class metadata have been removed.

原空间容量
 

        默认情况下,类元数据的分配受可用本地内存数量的限制,(容量取决于32位JVM还是64位JVM,以及操作系统虚拟内存可用性)。
        新标志(MaxMetaspaceSize)用于限制用于类元数据的本地内存大小。如果不指定此标志,Metaspace将根据应用程序运行时的需求动态调整大小。

Metaspace capacity

  • By default class metadata allocation is limited by the amount of available native memory (capacity will of course depend if you use a 32-bit JVM vs. 64-bit along with OS virtual memory availability).
  • A new flag is available (MaxMetaspaceSize), allowing you to limit the amount of native memory used for class metadata. If you don’t specify this flag, the Metaspace will dynamically re-size depending of the application demand at runtime.

原空间垃圾收集

        一旦类元数据使用达到“MaxMetaspaceSize”,就会触发僵死以及类加载器的垃圾收集。
        为了限制此类垃圾收集的频率或延迟,显然需要对元空间进行适当的监视和调优。元空间垃圾收集过多原因可能是类、类加载器内存泄漏或应用程序大小调整不足造成的。

Metaspace garbage collection

  • Garbage collection of the dead classes and classloaders is triggered once the class metadata usage reaches the “MaxMetaspaceSize”.
  • Proper monitoring & tuning of the Metaspace will obviously be required in order to limit the frequency or delay of such garbage collections. Excessive Metaspace garbage collections may be a symptom of classes, classloaders memory leak or inadequate sizing for your application.

Java堆空间的影响

      一些杂项数据已经移到Java堆空间。这意味着在将来JDK 8升级之后,Java堆空间会有所增加。

Java heap space impact

  • Some miscellaneous data has been moved to the Java heap space. This means you may observe an increase of the Java heap space following a future JDK 8 upgrade.

原空间管程

在HotSpot 1.8详细GC日志输出中可以使用元空间。
基于我们对b75的测试,Jstat和JVisualVM目前还没有更新,旧的PermGen空间引用仍然存在。

Metaspace monitoring

  • Metaspace usage is available from the HotSpot 1.8 verbose GC log output.
  • Jstat & JVisualVM have not been updated at this point based on our testing with b75 and the old PermGen space references are still present.

足够的理论,现在,让我们看看这个新的内存空间通过我们泄漏的Java程序运行…
Enough theory now, let’s see this new memory space in action via our leaking Java program…

PermGen与Metaspace运行时比较

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

将测试下列方案:

使用JDK 1.7运行Java程序,以监视和耗尽设置为128 MB的永久内存空间。
使用JDK 1.8 (b75)运行Java程序,以监视新元空间的动态增长和垃圾收集。
使用JDK 1.8 (b75)运行Java程序,通过将MaxMetaspaceSize值设置为128 MB来模拟元空间的耗尽。

PermGen vs. Metaspace runtime comparison

In order to better understand the runtime behavior of the new Metaspace memory space, we created a class metadata leaking Java program. You can download the source here.

The following scenarios will be tested:

  • Run the Java program using JDK 1.7 in order to monitor & deplete the PermGen memory space set at 128 MB.
  • Run the Java program using JDK 1.8 (b75) in order to monitor the dynamic increase and garbage collection of the new Metaspace memory space.
  • Run the Java program using JDK 1.8 (b75) in order to simulate the depletion of the Metaspace by setting the MaxMetaspaceSize value at 128 MB.

JDK 1.7 @64位-永久代耗尽测试

  • 配置了50K迭代的Java程序
  • Java堆空间为1024 MB
  • Java PermGen空间为128 MB  (-XX:MaxPermSize=128m)

JDK 1.7 @64-bit – PermGen depletion

  • Java program with 50K configured iterations
  • Java heap space of 1024 MB
  • Java PermGen space of 128 MB (-XX:MaxPermSize=128m)

Java 8: 从永久代到原空间

正如您可以从JVisualVM中看到的,在加载了大约30K+类之后,永久代消耗达到了极限。我们还可以从程序和GC输出中看到这种消耗。

As you can see form JVisualVM, the PermGen depletion was reached after loading about 30K+ classes. We can also see this depletion from the program and GC output.

1

2

3

4

5

6

7

Class metadata leak simulator

 

Author: Pierre-Hugues Charbonneau

 

http://javaeesupportpatterns.blogspot.com

 

ERROR: java.lang.OutOfMemoryError: PermGen space

Java 8: 从永久代到原空间

现在让我们使用HotSpot JDK 1.8 JRE执行这个程序。

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

配置了50K迭代的Java程序
Java堆空间为1024 MB
Java元空间:*(默认)

Now let’s execute the program using the HotSpot JDK 1.8 JRE.

JDK 1.8 @64-bit – Metaspace dynamic re-size

  • Java program with 50K configured iterations
  • Java heap space of 1024 MB
  • Java Metaspace space: unbounded (default)

Java 8: 从永久代到原空间

Java 8: 从永久代到原空间

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

As you can see from the verbose GC output, the JVM Metaspace did expand dynamically from 20 MB up to 328 MB of reserved native memory in order to honor the increased class metadata memory footprint from our Java program. We could also observe garbage collection events in the attempt by the JVM to destroy any dead class or classloader object. Since our Java program is leaking, the JVM had no choice but to dynamically expand the Metaspace memory space. The program was able to run its 50K of iterations with no OOM event and loaded 50K+ Classes. Let’s move to our last testing scenario..

JDK 1.8 @64位-永久代耗尽测试

配置了50K迭代的Java程序
Java堆空间为1024 MB
Java元空间:128 MB (-XX:MaxMetaspaceSize=128m)

JDK 1.8 @64-bit – Metaspace depletion

  • Java program with 50K configured iterations
  • Java heap space of 1024 MB
  • Java Metaspace space: 128 MB (-XX:MaxMetaspaceSize=128m)

Java 8: 从永久代到原空间

Java 8: 从永久代到原空间

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

As you can see form JVisualVM, the Metaspace depletion was reached after loading about 30K+ classes; very similar to the run with the JDK 1.7. We can also see this from the program and GC output. Another interesting observation is that the native memory footprint reserved was twice as much as the maximum size specified. This may indicate some opportunities to fine tune the Metaspace re-size policy, if possible, in order to avoid native memory waste.

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

Now find below the Exception we got from the Java program output.

1

2

3

4

5

6

7

Class metadata leak simulator

 

Author: Pierre-Hugues Charbonneau

 

http://javaeesupportpatterns.blogspot.com

 

ERROR: java.lang.OutOfMemoryError: Metadata space

完成!

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

Done!

As expected, capping the Metaspace at 128 MB like we did for the baseline run with JDK 1.7 did not allow us to complete the 50K iterations of our program. A new OOM error was thrown by the JVM. The above OOM event was thrown by the JVM from the Metaspace following a memory allocation failure.

#metaspace.cpp

Java 8: 从永久代到原空间

结束语

我希望您能够欣赏这个早期的Java 8元空间分析和实验。当前的观察结果明确表明,需要进行适当的监视和调优,以避免出现由上一个测试场景触发的过多元空间GC或OOM条件等问题。将来的文章可能会包括性能比较,以确定与此新特性相关的潜在性能改进。

Final words

I hope you appreciated this early analysis and experiment with the new Java 8 Metaspace. The current observations definitely indicate that proper monitoring & tuning will be required in order to stay away from problems such as excessive Metaspace GC or OOM conditions triggered from our last testing scenario. Future articles may include performance comparisons in order to identify potential performance improvements associated with this new feature.
 

Reference: Java 8: From PermGen to Metaspace from our JCG partner Pierre-Hugues Charbonneau at the Java EE Support Patterns & Java Tutorial blog.