【jvm深入了解的高手回答下啊】jvm Perm区gc回收问题

fhqibjg 2013-02-17 06:12:40
我们知道jvm里堆空间划分为三个代:
年轻代(Young Generation)
年老代(Old Generation)
永久代(Permanent Generation)

年轻代和年老代是存储动态产生的对象。永久带主要是存储的是java的类信息,包括解析得到的方法、属性、字段等等。永久带基本不参与垃圾回收

现在问题出现了:

[Full GC (System) 3601.118: [CMS: 187025K->113740K(5242880K), 0.8019600 secs] 1129480K->113740K(7340032K), [CMS Perm : 57763K->50540K(131072K)], 0.8024830 secs] [Times: user=0.80 sys=0.00, real=0.80 secs]

1,当出现Fullgc后,明显看到perm己发生过回收,且系统中会出现反射调用失败的一些提示信息。

2,在配制cms回收时,看到Perm开启CMS回收Perm区选项:
+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled


也就是说perm区是可以回收的,那perm区可回收这与jdk版有关系(原老版本是不可以)?还有perm区回收是主要回收什么(全部都回收还是只收收部分)?

...全文
917 20 打赏 收藏 转发到动态 举报
写回复
用AI写文章
20 条回复
切换为时间正序
请发表友善的回复…
发表回复
fhqibjg 2013-03-13
  • 打赏
  • 举报
回复
这里再贴下一个网站上看到的文章 源文:http://fallenlord.blogbus.com/logs/57543373.html 众所周知,Java从1.2开始引入分带GC策略,JVM内存被分成了3个带:young generate、tenured generation和permanent generation 前面两个带相信大家已经非常熟悉了,一般我们所说的GC主要是在这两个带里面运作,我们这里主要讨论Permanent Generation Perm带是存储类元数据信息的地方,一直以来大家都认为是不会被GC的——确实,类元数据信息被回收了别的类怎么玩? 要说明这个问题,主要需要弄清楚Perm带除了元数据信息外还存了些什么? 栈存基本类型和引用、堆存对象,这个简单的道理大家都懂,但真的是所有的基本类型都存在栈里吗?不见得 还是那句话——无码无真相,我们先从最简单的例子来看一个问题,下面这段代码相信大家都看过很多遍了: @Test public void literal() { String a = "abc"; String b = "abc"; Assert.assertTrue(a == b); } 没啥好说的,字符串字面量在编译期就会被编译器直接植入.class文件常量池中,并在运行期被JVM当做常量加载,所有存储超过一个字节大小的基本类型都会被编译器优化成这样,这点用javap反编译看下汇编如何压栈的就知道了。关键问题是,常量池在运行期是放在堆里的还是放在栈里的?——答案是都不在 JVM Spec中的Runtime Data Area分为5个区域:pc register、java stack、native stack、java heap、method area,前三个和大多数语言类似比较容易理解,java Heap就是我们常说的堆了,也是Young Generation和Tenured Generation所在,而Method Area就是我们所说的Permanent Generation,上面代码中的字面字符串常量就存在了这里(也有人认为PermGen属于广义上的Heap) 不信?OK,看看下面的代码: @Test public void permGenOOM() { List<String> list = new ArrayList<String>(); for (int i = 0; i < Integer.MAX_VALUE; i++) { String t = String.valueOf(i).intern(); list.add(t); } } 运行前先设置下JVM参数:-XX:PermSize=2M -XX:MaxPermSize=4M,将PermSize调小点,这样比较容易出结果 执行一下,PermGen应该很快就爆了 这里用到了String的intern方法,作用我就不多说了,如果不了解的可以自己看看JDK API,大致就是将一个字符串变量转存到常量区的String Pool中,和直接写字面常量是一样的效果,以下代码可以证明: @Test public void literalAndIntern() { String a = "abc"; String b = new String("abc"); Assert.assertFalse(a == b); Assert.assertTrue(a == b.intern()); } OK,既然知道了Permanent Generation中还存着这个东东,那么我们就可以试验GC了 去掉上一段代码的第3和第6行,也就是整个程序不再持有创建出来的intern对象的引用,使得对象可以被GC,同事在JVM参数中追加GC观察-verbose:gc -XX:+PrintGCDetails @Test public void permGenGC() { for (int i = 0; i < Integer.MAX_VALUE; i++) { String t = String.valueOf(i).intern(); } } 运行代码,这次是不是没有PermGen OOM了?观察Console中的GC日志,看到很多minor GC,我们主要关注Major GC(Full GC)的内容: [Full GC [Tenured: 340K->340K(4096K), 0.0197170 secs] 959K->340K(5056K), [Perm : 4096K->799K(4096K)], 0.0199235 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 看到Perm段,显示PermGen总大小为4096K,此次full gc将其从4096K清理到了799K 到这里应该已经大功告成了,这篇帖子的主题也达到了——GC会清理PermGen 但事情还不算完,既然GC会去动PermGen,那是否会清理类元数据信息呢?虽然看起来很荒谬的理论,但是还是值得尝试一下的: @Test public void permGenCglibOOM() { for (int i = 0; i < Integer.MAX_VALUE; i++) { createInstance(); } } private static ValueObject createInstance() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(ValueObject.class); enhancer.setUseCache(false); // 关闭CGLib缓存,否则总是生成同一个类 enhancer.setCallback(new MethodInterceptor() { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { return proxy.invokeSuper(obj, args); } }); return (ValueObject) enhancer.create(); } public static class ValueObject { private String username = "guolin"; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } } 这里我们仿造Hibernate用CGLib动态构造了一堆ValueObject的子类,如愿以偿,很快PermGen就OOM了,观察Full GC日志,PermGen一点都没压下去 [Full GC [Tenured: 1950K->1560K(4096K), 0.0323010 secs] 2637K->1560K(5056K), [Perm : 4095K->4095K(4096K)], 0.0323519 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] 那么现在结论很明显了: GC仅会清理PermGen中的常量池信息,而不会清理类元数据信息
fhqibjg 2013-02-20
  • 打赏
  • 举报
回复
感觉class被卸载,在作为服务器的工程中好难看到。原先做过的一个服务部件,在用jconsole对其监控好几天都没看到类被卸载过。特别是 2.加载该类的ClassLoader已经被回收。 classloader被卸载也要满足那些实例没有对该加载器的引用,就这点来说一般也只有自定义的classload才会回收。而该系统级的ClassLoader永远都不会被卸载,除非JVM挂了或是使用了动态部署。 希望还有高人能给我们更清楚详细的解说下
dracularking 2013-02-19
  • 打赏
  • 举报
回复
引用 13 楼 ticmy 的回复:
第二点我倒是觉得蛮难符合条件,“该类”指的是要被卸载的类
我现在还没搞清ClassLoader卸载的条件是什么
dracularking 2013-02-19
  • 打赏
  • 举报
回复
引用 15 楼 fhqibjg 的回复:
谢谢各位帮助,先结贴了有问题继续探讨
期待探讨,我开帖子也行啊
dracularking 2013-02-19
  • 打赏
  • 举报
回复
引用 14 楼 fhqibjg 的回复:
迟迟没有被执行,但不代表将来不会被执行 ,即然这个代码是某个条件分支,这就存在着引用。当这个引用的类没有回收前,这个类例实可以肯定是绝对不会回收。
你说的“引用”不是一般意义上的引用吧? 这里内存中根本还没有这个类的实例存在,如果你说的引用是指 “在代码中存在”,且有潜在被实例化的可能性,这样的话这种类几乎就不可能被卸载了,因为类既然设计存在了,一般都要去使用的,即使是潜在使用。
fhqibjg 2013-02-19
  • 打赏
  • 举报
回复
谢谢各位帮助,先结贴了有问题继续探讨
fhqibjg 2013-02-19
  • 打赏
  • 举报
回复
引用 12 楼 dracularking 的回复:
引用 11 楼 ticmy 的回复:看撒迦回复的最后:http://hllvm.group.iteye.com/group/topic/34986# 他说到的貌似都是有关反射是否引用到该类的情况,类不会被回收。 我说的是全局唯一的 new SomeClassName();代码隐藏于某个条件分支中,迟迟没有被执行,但不代表将来不会被执行。 这也意味着当前堆中没……
迟迟没有被执行,但不代表将来不会被执行 ,即然这个代码是某个条件分支,这就存在着引用。当这个引用的类没有回收前,这个类例实可以肯定是绝对不会回收。但是当引用这个类的类回收了,这时这个实列在stack中就没有了引用地址的存在,在gc标记的时候就可以当作无用对象标记出来。我们在配制gc时一般都有一个gc的触发条件(如老年代或是静态区空间不够时,或cms配制了CMSInitiatingOccupancyFraction触发的伐值时),gc时就会被引用标记成无用对象然后删除。 想要class也加收,除了上面实例回收外还要满足 1.加载该类的ClassLoader已经被GC。 2.该类对应的java.lang.Class 对象没有在任何地方被引用,如不能在任何地方通过反射访问该类的方法。 我觉得这也就是所谓的类卸载吧(当类中有static修饰时,这时要想static的也回收即class回收)
龙四 2013-02-18
  • 打赏
  • 举报
回复
引用 8 楼 fhqibjg 的回复:
引用 7 楼 ticmy 的回复:引用 6 楼 fhqibjg 的回复:谢谢楼上各位给的答案和参考链接,但还是不能解决我的疑问,我想要知道更多关于perm区gc的详细。网上查找也尽是年轻代(Young Generation)、年老代(Old Generation)的何时被gc,gc时的过程与算法。而关于静态区的都末提到。perm区的东西如何被判断为无用对象可以回收了,我想……
如果类被回收,静态变量肯定也没了
fhqibjg 2013-02-18
  • 打赏
  • 举报
回复
引用 7 楼 ticmy 的回复:
引用 6 楼 fhqibjg 的回复:谢谢楼上各位给的答案和参考链接,但还是不能解决我的疑问,我想要知道更多关于perm区gc的详细。网上查找也尽是年轻代(Young Generation)、年老代(Old Generation)的何时被gc,gc时的过程与算法。而关于静态区的都末提到。perm区的东西如何被判断为无用对象可以回收了,我想这与堆中的回收是有区别的吧!最起码……
那就是说perm区的回收和Java堆中的对象非常类似(标记清除算法也一样),不管是否static,只要类己被回收那这类中static定义的一切也会回收。
龙四 2013-02-18
  • 打赏
  • 举报
回复
引用 6 楼 fhqibjg 的回复:
谢谢楼上各位给的答案和参考链接,但还是不能解决我的疑问,我想要知道更多关于perm区gc的详细。网上查找也尽是年轻代(Young Generation)、年老代(Old Generation)的何时被gc,gc时的过程与算法。而关于静态区的都末提到。perm区的东西如何被判断为无用对象可以回收了,我想这与堆中的回收是有区别的吧!最起码它们存放的东西就不尽相同 ……
这不就是判断条件么: □该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。 □加载该类的ClassLoader已经被回收。 □该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
fhqibjg 2013-02-18
  • 打赏
  • 举报
回复
谢谢楼上各位给的答案和参考链接,但还是不能解决我的疑问,我想要知道更多关于perm区gc的详细。网上查找也尽是年轻代(Young Generation)、年老代(Old Generation)的何时被gc,gc时的过程与算法。而关于静态区的都末提到。perm区的东西如何被判断为无用对象可以回收了,我想这与堆中的回收是有区别的吧!最起码它们存放的东西就不尽相同
龙四 2013-02-18
  • 打赏
  • 举报
回复
引用 2 楼 fhqibjg 的回复:
还有就是想知道,回收后那些静态类或静态变量怎么办?如果再次调用到静态方法,是否重新classload到内存新开辟空间
所以说类的回收条件是及其苛刻的: 只有无用的类才可以回收,类需要同时满足下面3个条件才能算是“无用的类”: □该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。 □加载该类的ClassLoader已经被回收。 □该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
龙四 2013-02-18
  • 打赏
  • 举报
回复
引用 12 楼 dracularking 的回复:
引用 11 楼 ticmy 的回复:看撒迦回复的最后:http://hllvm.group.iteye.com/group/topic/34986# 他说到的貌似都是有关反射是否引用到该类的情况,类不会被回收。 我说的是全局唯一的 new SomeClassName();代码隐藏于某个条件分支中,迟迟没有被执行,但不代表将来不会被执行。 这也意味着当前堆中没……
第二点我倒是觉得蛮难符合条件,“该类”指的是要被卸载的类
dracularking 2013-02-18
  • 打赏
  • 举报
回复
引用 11 楼 ticmy 的回复:
看撒迦回复的最后:http://hllvm.group.iteye.com/group/topic/34986#
他说到的貌似都是有关反射是否引用到该类的情况,类不会被回收。 我说的是全局唯一的 new SomeClassName();代码隐藏于某个条件分支中,迟迟没有被执行,但不代表将来不会被执行。 这也意味着当前堆中没有包含该类实例,符合条件1; 该类从来没有被加载过,条件2应该也符合; 条件3如果说的只是反射方式引用的话,那也符合。 3个条件都符合的情况下,这种类会被回收?感觉不应该被回收才对。
龙四 2013-02-18
  • 打赏
  • 举报
回复
引用 10 楼 dracularking 的回复:
引用 4 楼 ticmy 的回复:所以说类的回收条件是及其苛刻的: 只有无用的类才可以回收,类需要同时满足下面3个条件才能算是“无用的类”: 1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。 2.加载该类的ClassLoader已经被回收。 3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问……
看撒迦回复的最后:http://hllvm.group.iteye.com/group/topic/34986#
dracularking 2013-02-18
  • 打赏
  • 举报
回复
引用 4 楼 ticmy 的回复:
所以说类的回收条件是及其苛刻的: 只有无用的类才可以回收,类需要同时满足下面3个条件才能算是“无用的类”: 1.该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。 2.加载该类的ClassLoader已经被回收。 3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
我没看过类回收条件,但如果这样3条,感觉也不能杜绝类再次被使用,难道它是不管这个的,是基于一种概率条件的回收? 比如某个方法中有new一个类的代码,但因为条件不满足迟迟没有被调用,这样的类也会被回收吗?
dracularking 2013-02-17
  • 打赏
  • 举报
回复
引用 2 楼 fhqibjg 的回复:
还有就是想知道,回收后那些静态类或静态变量怎么办?如果再次调用到静态方法,是否重新classload到内存新开辟空间
如果是真的因为某些原因被回收了,要再用那估计就是要重新加载了(除非回收是基于保证不再使用,但不知道这能否实现),就像初始化时一样。
fhqibjg 2013-02-17
  • 打赏
  • 举报
回复
还有就是想知道,回收后那些静态类或静态变量怎么办?如果再次调用到静态方法,是否重新classload到内存新开辟空间
龙四 2013-02-17
  • 打赏
  • 举报
回复
不同版本的jvm,甚至是不同的小版本,gc的算法与机制都有可能不同 关于类卸载:http://hllvm.group.iteye.com/group/topic/35342

50,542

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧