介绍
在本快速教程中,我们将研究Java环境中PermGen和Metaspace内存区域之间的差异。
重要的是要记住,从Java 8开始,Metaspace取代了PermGen,这带来了一些实质性的变化。
永久代
PermGen(Permanent Generation)是与主内存堆分开的特殊堆空间。
JVM跟踪PermGen中已加载的类元数据(包括类的名称、方法信息、字段信息)。此外,JVM将所有静态内容存储在此内存区域中。这包括所有静态方法、静态变量和对静态对象的引用。
此外,它还包含有关字节码和JIT信息的数据。 在Java 7之前,字符串常量池也是该内存的一部分。
32位JVM的默认最大内存大小为64MB,而64位版本的默认最大内存大小为82MB。
但是,我们可以使用JVM选项更改默认大小:
-XX:PermSize=[size] PermGen空间的初始或最小值
-XX:MaxPermSize=[size] 最大值
最重要的是,Oracle在JDK 8版本中完全删除了此内存空间。 因此,如果我们在Java 8和更高版本中使用这些调整标志,则会收到以下警告:
>> java -XX:PermSize=100m -XX:MaxPermSize=200m -version
OpenJDK 64-Bit Server VM warning: Ignoring option PermSize; support was removed in 8.0
OpenJDK 64-Bit Server VM warning: Ignoring option MaxPermSize; support was removed in 8.0
...
由于其有限的内存大小,PermGen可能出现著名的OutOfMemoryError的错误。 简而言之,类加载器没有正确地被垃圾收集器收集,导致了内存泄漏。
因此,我们收到一个内存空间错误;在创建新的类加载器时,这种情况主要发生在开发环境中。
元空间
简而言之,Metaspace是一个新的内存空间 – 从Java 8版本开始;它已替换了较旧的PermGen内存空间。 最重要的区别是它如何处理内存分配。 具体来说,默认情况下,此本地内存区域会自动增长。
我们还有新的标志来调整内存:
- MetaspaceSize和MaxMetaspaceSize – 我们可以设置的Metaspace上限。
- 达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。
- MinMetaspaceFreeRatio - 是垃圾收集后可用的类元数据容量的最小百分比。
- 在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集
- MaxMetaspaceFreeRatio - 是垃圾回收后为避免空间量减少而释放的类元数据容量的最大百分比。
- 在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集。默认是没有限制的 此外,垃圾收集过程还从此更改中获得了一些好处。现在,一旦类元数据使用量达到其最大元空间大小,垃圾收集器就会自动触发清除死类。
因此,通过此改进,JVM减少了出现OutOfMemory错误的机会。
尽管有这些改进,我们仍然需要监视和调整元空间以避免内存泄漏。
总结
在这篇快速文章中,我们简要介绍了PermGen和Metaspace内存区域。此外,我们解释了它们之间的主要区别。
PermGen在JDK 7和更早版本中仍然存在,但是Metaspace为我们的应用程序提供了更灵活,更可靠的内存使用。
- 方法区逻辑上是堆的一部分,并且方法区是在JVM规范中定义的,而永久代是HotSpot虚拟机的方法区实现。
- Java7之前,方法区位于永久代,和堆相互隔离,永久代的大小在启动JVM时可以设置一个固定值,不可变;
- Java7中,字符串常量池从永久代移到堆中;字符串常量池存储的是字符串对象的引用,字符串实例是在堆中;
- Java8中,取消永久代,方法存放于元空间(Metaspace),元空间与堆不相连,可以自动增长;字符串常量池是在本地内存当中,存储的也只是引用。