今天粗略的看了下java的gc, http://calvin.iteye.com/blog/91905。以前也看过一些,但是怎么找到测试的环境来验证参数的改变带来的变化呢。有一点要说名的是jvm规范并未说明java gc采取什么样的策略,所以在各个jvm的实现也有所不同。在了解基本概念之前,其实我们可以想想,gc主要就是负责那些占用了内存资源的对象的管理,或者说是对垃圾对象(不再使用)的回收java的gc说白了就是对java内存(也就是堆的管理)。因此肯定会对这些对象的生命周期不同而分类,分完类后就会采用什么样的策略去处理这些垃圾对象。
基本概念:
根据对象的生命周期分类:young,old,permanent。
young:新生代又分为Eden 和两片生存空间(survivor spaces)。所有新建对象都放在eden:两片生存区中保证有一片空间在任何时间是空的,当垃圾收集发生时,加入suvivor1是空的,survivor2里面是原先有的对象。当发生垃圾回收时,先把 Eden 中的活的对象复制到survivor1,再把survivor2复制到survivor1,然后清理eden和survior2.直到到达最大门限值(老化),然后复制到旧生代(old)。可以用-Xmn参数规定其大小。
old:young总是不断膨胀的,其中有些对象在young中发生的几次收集中始终都在survivor中存活,终于survivor的空间也撑不住了,于是这些对象被搬到了old区了(注:也有可能是young区的对象被收集了几次后被搬到old区,这个几次由参数-XX:MaxTenuringThreshold=参数 来设置)。当然old区也不是说这里面的对象就一直有效,old也是要垃圾清理的,但是young的垃圾清理不同,old区采用的是标记清理。所谓标记清理,就是先遍历一遍所有的对象,看看那些是活跃那些是垃圾,然后再遍历一次,清理垃圾。
Permanent,持久代。装载Class信息等基础数据,默认64M,如果是类很多很多的服务程序,需要加大其设置-
XX:MaxPermSize=,否则它满了之后会引起fullgc()或Out of Memory。 像Spring,Hibernate这类喜欢动态生成类的框架
需要更多的持久代内存。
young是一个比较活跃的区域,因此gc在对young收集频率很高,这种收集叫minor collection,而且使用的是复制算法。
而对old代则使用的是标记整理(或标记压缩整理),这种成本比较大。同时对young和old收集则是major collection。
GC收集器除了在具体执行回收的时候很容易理解外,因为策略的不同又有以下几种分类:
A:串行收集器: 使用 -XX:+UseSerialGC,策略为年轻代串行复制,年老代串行标记整理。这种方法很老。
B:吞吐量优先的并行收集器:使用 -XX:+UseParallelGC ,也是JDK5 -server的默认值。策略为:
1.年轻代暂停应用程序,多个垃圾收集线程并行的复制收集,线程数默认为CPU个数,CPU很多时,可用–
XX:ParallelGCThreads=减少线程数。多cpu时代的选择,呵呵。
2.年老代暂停应用程序,与串行收集器一样,单垃圾收集线程标记整理。
可以使用-XX:MaxGCPauseMillis= 和 -XX:GCTimeRatio 来调整GC的时间。
需要注意的是,不管是年轻代还是年老代都需要短暂的暂停应用。
C:暂停时间优先的并发收集器(Concurrent Low Pause Collector-CMS)
使用-XX:+UseConcMarkSweepGC,简称CMS.策略为:
1.年轻代同样是暂停应用程序,多个垃圾收集线程并行的复制收集。
还是需要暂停应用,这个就是大型系统的瓶颈所在,特别是当年轻代变大的时候,整个复制时间长也就意味着
暂停应用时间长。太小的话又会导致频繁的垃圾回收。
2.年老代则只有两次短暂停,其他时间应用程序与收集线程并发的清除。
这个改进比较大,大部分垃圾回收的时候应用是运行的。
3.持久代也用CMS去收集而不是full gc。
在涉及到调优gc的时候,先要观察一下暂停时间显示,有了数据我们才好说话。
加入下列参数 (请将PrintGC和Details中间的空格去掉,CSDN很怪的认为是禁止字句)
-verbose:gc -XX:+PrintGC Details -XX:+PrintGCTimeStamps
会程序运行过程中将显示如下输出
9.211: [GC 9.211: [ParNew: 7994K->0K(8128K), 0.0123935 secs] 427172K->419977K(524224K), 0.0125728 secs]
显示在程序运行的9.211秒发生了Minor的垃圾收集,前一段数据针对新生区,从7994k整理为0k,新生区总大小为8128k,
程序暂停了12ms,而后一段数据针对整个堆。
对于年老代的收集,暂停发生在下面两个阶段,CMS-remark的中断是17毫秒:
[GC [1 CMS-initial-mark: 80168K(196608K)] 81144K(261184K), 0.0059036 secs]
[1 CMS-remark: 80168K(196608K)] 82493K(261184K),0.0168943 secs]
再加两个参数 -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime对暂停时间看得更清晰
。