简单介绍
永久代是JDK7及以前对方法区的一个JVM实现。主要存放的是类信息、字符串常量、静态常量等(PS:JDK7之后比如字符串常量池等信息移到了堆内,不在永久代存储了)
元空间是JDK8之后才有,目的是取代永久代。
区别
对比项 | 永久代PermGen | 元空间Metaspace |
---|---|---|
支持JDK版本 | JDK7及以前 | JDK8及以后 |
JVM参数 | -XX:PermSize 初始永久代大小 -XX:MaxPermSize : 最大永久代大小。默认32位64M,64位82M(指针膨胀) 注意:MaxPermSize设置,jvm启动的时候就会申请MaxPermSize大小的内存。 |
-XX:MetaspaceSize,初始空间配额,单位bytes -XX:MaxMetaspaceSize,分配的最大空间。默认是没有限制的。 -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比 -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比。 注意:MaxMetaspaceSize设置,jvm启动的时候不会申请这么大一块内存,与MaxPermSize是有区别的 |
OOM | 受限于默认值大小,易产生OOM。虽然有参数可以指定永久代大小,但是因为存储内容较多,比较难预估出来一个合适的值。 | 受限于本机内存大小,OOM概率小。 |
GC | 永久代和老年代GC绑定到一起,二者其一满了之后便可触发永久代和老年代的垃圾回收 JDK7以后,如果使用G1垃圾回收器,永久代的垃圾回收只有在FullGC的时候才会触发。G1仅仅在PermGen满了或者应用分配内存的速度比G1并发垃圾收集速度快的时候才触发FullGC (可参见Oracle博客说明 及文末引用地址说明) CMS回收器下,可以使用-XX:+CMSClassUnloadingEnabled 参数来让永久代的垃圾回收可以在CMC并发回收阶段处理掉。这点和G1不同。 |
当metaspace使用达到最大值的时候,会自动触发GC。但不影响堆上GC。 |
归属 | 不属于堆内存,但内存空间和堆内存相连(存疑) | 不属于堆内存,属于Native Memory,受限于服务器内存 |
补充 | 虽然可能OOM但不至于导致服务器出现问题(因为有永久代大小限制) | 内存默认无上限,受限于服务器内存,所以可能会导致服务器出现问题,需要监控Metaspace的使用情况 |
移除永久代原因
-
减少永久代OOM的情况。
永久代存放的一些类信息等,在动态生成类之前基本是足够的,但是随着动态类生成的技术的发展,原本的类信息量及大小变的开始不再那么永久不变更了。另外最开始的永久代里还存放字符串常量池,这块的增长也不可控,所以也就可能出现OOM的情况。(PS:JDK7已经开始了字符串常量池、符号引用、静态变量从永久代的迁出)
-
降低GC复杂度
永久代和老年代的GC绑定到一起,但是永久代的内容被GC的可能性要小于老年代,所以,对GC而言,一方面效率不高,另外一方面,复杂度也高了很多(metaspace上gc不需要做压缩和扫描)。
-
商业合并
这个应该是最主要的原因。 现有主流的JVM实现中,除了Hotspot之外,基本没有永久代这个概念,但仍旧运行的很好,如 JRockit。之后Oracle收购了Jrockit之后,便开始了Hotspot和JRockit的合并(非代码合并,而是取了JRockit优点做开发)。于是,在JDK8之后,便取消掉了永久代。
PS:
方法区 不等同于 永久代。 永久代是对方法区的一种实现。
永久代和堆逻辑上是分开的。 一般我们称永久代叫“非堆”,但物理上内存是相连的。
关于永久代和堆的关系
-
永久代在不在堆里?
各有各的说法,各有各的理论。 我理解是内存相连,但逻辑分开。
推荐看几篇文章了解下各自观点
方法区的Class信息,又称为永久代,是否属于Java堆? - 毛海山的回答 - 知乎 https://www.zhihu.com/question/49044988/answer/113961406
https://stackoverflow.com/questions/41358895/permgen-is-part-of-heap-or-not
https://stackoverflow.com/questions/1279449/what-is-perm-space
https://stackoverflow.com/questions/4848669/perm-space-vs-heap-space
-
Xmx不包含永久代大小
测试
测试代码:
public class PermGenTest {
private final static int ONE_MB = 1024*1024;
/**
* JVM参数: -xmx20m -xms20m -xmn10m -XX:MaxPermSize=30m -XX:PermSize=30m -XX:PretenureSizeThreshold=100
* @param args
*/
public static void main(String[] args) {
//连续申请3M内存
for (int i =0 ; i < 100; i ++){
allocateMemory(3);
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void allocateMemory(int size){
for (int i =0 ; i < size ; i++){
byte[] bytes = new byte[ONE_MB];
}
}
}
1、测试老年代和永久代任意一方满了之后,会触发永久代垃圾回收。
测试环境: JDK1.6/1.7 + CMS垃圾回收 (以JDK1.6为例,1.7的结果与下面类似)
关于代码的说明:
使用-XX:PretenureSizeThreshold=100
限定只要对象大小超过100字节,就会直接分配到老年代。代码中,我们连续申请3M大小内存,所以很快会触发CMS的老年代垃圾回收。
JVM参数如下:
/opt/soft/jdk/jdk1.6.0_45/bin/java -verbose:gc -Xloggc:gc.l -XX:+PrintGCDetails -Djava.rmi.server.hostname=192.168.1.x -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=33306 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Xmx20m -Xmn10m -Xms20m -XX:MaxPermSize=30m -XX:PermSize=30m -XX:PretenureSizeThreshold=100 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC PermGenTest
产生的GC结果:
可以看到,当老年代触发CMS垃圾回收的时候,永久代也会被回收(PS:图中永久代没有可被回收的东西,所以数值没变化)
如果我们把-XX:PretenureSizeThreshold
调整成5M,则对象不会进入到老年代,这时候年轻代GC,并没有同时触发对永久代的GC。(如下图,可以看到没有Perm的回收日志)
Ps:
-XX:MaxTenuringThreshold只对串行回收器和ParNew有效,对ParallGC无效
2、测试G1和CMS的永久代垃圾回收策略
测试环境: JDK1.7 + G1垃圾回收 (CMS的已在上面做过测试,此处查看G1和CMS触发垃圾回收场景)
代码不变,JVM参数调整为G1垃圾回收器
JVM参数:
/opt/soft/jdk/jdk1.7.0_80/bin/java -verbose:gc -Xloggc:gc.l -XX:+PrintGCDetails -Djava.rmi.server.hostname=192.168.1.x -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=33306 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Xmx20m -Xmn10m -Xms20m -XX:PretenureSizeThreshold=100 -XX:+UseG1GC PermGenTest
PS: G1下不建议设置Xmn。此处为了测试和CMS的对比,保留xmn参数。
整个的GC过程,并没有对永久代进行回收。
但如果FullGC时,则会回收永久代
所以也验证了前面所提到的永久代GC在不同的垃圾回收器下的不同表现。
参考
https://juejin.im/post/5df5fde36fb9a0162c486c71
https://www.cnblogs.com/paddix/p/5309550.html
https://cloud.tencent.com/developer/article/1415205
https://www.baeldung.com/java-permgen-metaspace
关于G1的Permgen说明:https://blogs.oracle.com/poonam/about-g1-garbage-collector%2c-permanent-generation-and-metaspace
https://segmentfault.com/a/1190000005036183
https://www.sczyh30.com/posts/Java/jvm-metaspace/
番外:
关于HotSpot和JRockit: https://www.zhihu.com/question/29265430/answer/43818804
关于JVM的问题,推荐到 https://blogs.oracle.com/ 查看,基本上是官方的人写的博客,可信度高。当然能力强的可以看JDK源码
TODO:
- CMS垃圾回收 https://www.cnblogs.com/littleLord/p/5380624.html
- 指针膨胀:https://my.oschina.net/u/2458458/blog/804654 https://gavinzhang1.gitbooks.io/java-jvm-us/content/diao_you_an_li_fen_xi_yu_shi_zhan.html
- Metaspace详解&坑:
- Metaspace碎片化问题:https://www.infoq.cn/article/Java-PERMGEN-Removed
- 推荐的几个JVM参数:https://www.ateam-oracle.com/recommended-jvm-parameters-for-11g-products
- 内存分布图:https://www.ateam-oracle.com/visualising-garbage-collection-in-the-jvm
- https://www.ateam-oracle.com/java-tuning-in-a-nutshell-part-1
- G1 https://zhuanlan.zhihu.com/p/54048685
关于JVM/JAVA相关的官方内容:https://www.ateam-oracle.com/ https://blogs.oracle.com/
https://www.linkedin.com/in/poonamparhar/
Hi Poonam Parhar
I found your blog from google, this is the link :https://blogs.oracle.com/poonam/about-g1-garbage-collector%2c-permanent-generation-and-metaspace
I have some questions to ask you , Can you do me a favor to explain these questions?
Questions:
- Is PermGen belongs to Java Heap? When JVM started ,it will allocate memory from OS, this memory contians heap and permgen memory ? And is the heap memory address adjacent to permgen memory address?
- Either OldGen or PermGen is full, permanent generation garbage collection will be triggered, Is that right? In Another word is "When triger permGen GC ?"
- what is MetaSpace's garbage collector? is G1? or CMS?
- Is metaspace gc not need gc scan and compacting?
- Is permgen gc more complex than metaspace gc ?
My English is not well ,If I don't make it clear , Please tell me .
Thank you very much ! I'm looking forward to your reply soon.