2015年3月13日

1,概述

   GC, Garbage Collection即垃圾回收,为什么会有垃圾回收呢?在jvm运行的时候,会使用大量的新生对象,然后这些新生对象在用完之后,以后可能会不被用到,但是他依然占据着内存空间,造成内存空间的浪费,为了使程序持久的运行,为以后的新生对象腾地方,需要及时的对这些“没用”的对象进行回收,垃圾回收就是专门用来处理这个问题的。

2,垃圾回收的原理

   垃圾回收首先要解决两个问题,1,判断一个对象为垃圾 。2,对被认为是垃圾的对象进行回收。

   如何判断一个对象是否为垃圾对象呢?

  (1)引用计数法,引用计数法就是在每个新生的对象上添加一个计数器,当该对象被引用的时候,计数器加1,当引用结束的时候,计数器减1,如果一个对象的计数器为0的时候,我们就可以认为该对象为“垃圾”。该办法的缺点是无法识别两个对象相互引用但与外界隔离的情况。比较早期的垃圾回收器采用这种方式来识别垃圾对象。

  (2)根集(Root set),现代的垃圾回收理论一般采用“根集”的概念来识别垃圾对象,什么是根集呢?就是选择一些对象作为根对象,如果从这些根对象无法到达的对象,就可以判断为垃圾,怎么理解“到达”呢?到达也就是引用,其中对象的属性可以引用另外一个对象,对象的方法参数及局部变量在活着的线程里面也可以引用另外一个对象,类的静态属性和方法也可以引用另外一个对象,根对象有哪些来源呢?jvm根对象的集合根据实现不同而不同,一般说来,有下列几种来源,1,局部变量中对象的引用和栈帧的操作数站(以及类变量中的对象引用)。2,被加载的类的常量池中对象的引用,比如字符串,被加载的类的常量池可能指向保存在堆中的字符串,比如类的名字,超类的名字,超接口的名字,字段名,方法名或者方法特征签名,3,传递到本地方法中的,没有被本地方法“释放”的对象引用。4,java虚拟机运行时数据区中从垃圾收集器的堆中分配的部分,在某些实现中,方法区中的类数据本身可能被存放在使用垃圾收集器的堆中,以便使用和释放对象同样的垃圾收集算法检测和卸载不再被引用的类。

   如何对垃圾对象进行回收呢?

  (1)Mark-Sweep,标记-清除,标记出垃圾对象,然后将其进行清除,该方法的缺点是容易产生内存碎片。也就是当jvm申请大块的连续内存空间的时候,会再次出发GC,从而导致效率下降。

  (2)Mark-Copy,标记-复制,标记出垃圾对象,然后将活着的对象copy到内存的另外一个区域,这样就可以腾出大块连续的内存空间。该方法把内存空间分为Eden,s0和s13块区域,通过将活对象在这3块区域中来回拷贝,来实现垃圾回收,一般将这3块的比例设置为8:1:1,缺点是,需要对内存划分特定功能的区域,也就是导致了可用的区域变小。

  (3)Mark-Compact,标记-整理,标记书垃圾对象,然后将活着的对象copy到内存另外一边,整理出大块连续的内存空间,该方法与标记-复制方法比较类似,但是无需在内存中划分出特定功能的区域。

3,什么时候会触发GC呢?

     一般说来GC是分区的,因为内存分为方法区,Young Generation和Old Generation,在每个区上面GC触发的条件是不一样的。

     Minor GC(Young Generation GC)触发的条件,Minor GC就是在新生代上进行的GC,当Eden区要满的时候,就进行一次GC。

     Major GC (Old Generation GC)触发的条件,Major GC也叫Full GC,因为当在Old Generation上执行一次GC的时候,一般会捎带着执行一次Minor GC,所以称为Full GC。该GC触发的条件: 1,Old Generation不足。2,permSpace不足。3,当从新生代晋升到Old Generation的对象大于Old Generation的剩余空间的时候。

     方法区的GC,一般说来,JVM会将方法区的实现并入Old Generation,所以,Major GC就包括了方法区的GC。这里需要注意一个问题。因为方法区里面存的是类,也就是说类的垃圾回收问题,也可以看做是类的卸载问题,在什么情况下,类被卸载或者说类被垃圾回收呢?满足下列条件的时候,类就会被回收(1)该类的所有实例已经被回收。(2)该类的Classloader被回收了。(3)该类的class对象没被任何对象使用,(即不存在Class.forName())。当着3种情况同时满足的时候,就会被垃圾回收。

4,常用的垃圾回收器

   由于垃圾回收是分区的,所以垃圾回收器也是分区域的。

 (1)新生代的垃圾回收器

  

垃圾收集器实现原理配置参数
Serial收集器停止复制算法,使用一个线程GC,其他线程暂停-XX:+UseSerialGC 可使用Serial+Serial Old模式垃圾回收
ParNew收集器停止复制算法,Serial多线程版,其他线程暂停,目的是缩短垃圾回收时间-XX:UseParNewGC来使用ParNew+Serial Old收集器组合收集内存;使用-XX:ParallelGCThreads来设置执行GC线程数
Parallel Scavenge 收集器停止复制算法,ParNew的优化版(个人认为),吞吐量优先的垃圾回收器-XX:UseParallelGC来控制Parallel Scavenge+Serial Old收集器组合回收垃圾(这也是默认Server模式下的垃圾回收器)


ParNew收集器和Parallel Scavenge收集器的区别。Parallel Scavenge收集器也是采用的停止复制算法和多线程收集,那么他跟ParNew有什么区别呢?笔者认为,其最大的不同时提供了两个设置参数,让用户根据自己的需要来设置GC停顿时间与吞吐量的平衡(吞吐量=运行用户代码的时间/总时间),减少了GC的时间,吞吐量就会下降,也就是说GC的次数会增加;提高了吞吐量,那么一次GC的时间就会变长,鱼和熊掌不可兼得,那么该GC的垃圾回收器是关注CPU的吞吐量,也就是充分运用CPU,适合运行后台运算,设置GC停顿时间的参数,-XX:MaxGCPauseMillis,吞吐量的比例,-XX:GCTimeRatio。

   除此之外,为了能充分的调节停顿时间与吞吐量的平衡,该GC回收器可以动态的设置新生代的大小,使用参数-XX:+UseAdaptiveSizePolicy,该垃圾回收器可以自动设置新生代中Eden和Survivor的大小及比例。以及从新生代到老年代晋升的条件。

   (2) 老年代的垃圾回收

 

垃圾回收器实现原理配置参数
Serial Old收集器Sweep和Compact,清理和压缩,清理垃圾对象,然后将存活的对象移动到另外一端, 
Parallel Old收集器Summary和Compact,多线程收集器,与Serial Old除了单线程与多线程的区别外,还有一个区别就是,Serial Old是清理垃圾对象,而这个是copy存活对象。使用-XX:UseParallelOldGC控制使用Parallel Scavenge+Parallel Old组合收集器
CMS(Concurrent Mark Sweep)收集器使用标记清除算法,多线程,优点是并发收集(用户线程与GC线程同时进行),目标是最短停顿时间,使用-XX:UseParallelOldGC 开关控制使用ParNew+CMS+Serial Old进行垃圾回收,优先使用ParNew+CMS,当用户线程内存不足的时候,采用备用方案Serial Old收集。

CMS收集的方法是:先3次标记,再1次清除,3次标记中前两次是初始标记和重新标记( 此时仍然需要停止(stop the world)), 初始标记(Initial Remark)是标记GC Roots能关联到的对象(即有引用的对象),停顿时间很短;并发标记(Concurrent remark)是执行GC Roots查找引用的过程,不需要用户线程停顿;重新标记(Remark)是在初始标记和并发标记期间,有标记变动的那部分仍需要标记,所以加上这一部分 标记的过程,停顿时间比并发标记小得多,但比初始标记稍长。在完成标记之后,就开始并发清除,不需要用户线程停顿。
所以在CMS清理过程中,只有初始标记和重新标记需要短暂停顿,并发标记和并发清除都不需要暂停用户线程,因此效率很高,很适合高交互的场合。
CMS也有缺点,它需要消耗额外的CPU和内存资源,在CPU和内存资源紧张,CPU较少时,会加重系统负担(CMS默认启动线程数为(CPU数量+3)/4)。
另外,在并发收集过程中,用户线程仍然在运行,仍然产生内存垃圾,所以可能产生“浮动垃圾”,本次无法清理,只能下一次Full GC才清理,因此在GC期间,需要预留足够的内存给用户线程使用。所以使用CMS的收集器并不是老年代满了才触发Full GC,而是在使用了一大半( 默认68%,即2/3,使用-XX:CMSInitiatingOccupancyFraction来设置)的时候就要进行Full GC,如果用户线程消耗内存不是特别大, 可以适当调高-XX:CMSInitiatingOccupancyFraction以降低GC次数,提高性能,如果预留的用户线程内存不够,则会触发Concurrent Mode Failure,此时,将触发备用方案:使用Serial Old 收集器进行收集,但这样停顿时间就长了,因此 -XX:CMSInitiatingOccupancyFraction不宜设的过大
还有,CMS采用的是标记清除算法,会导致内存碎片的产生,可以 使用-XX:+UseCMSCompactAtFullCollection来设置是否在Full GC之后进行碎片整理,用-XX:CMSFullGCsBeforeCompaction来设置在执行多少次不压缩的Full GC之后,来一次带压缩的Full GC

6,总结

    众多的垃圾回收器,没有最好的,只有最适合的,如果程序是计算型的应用程序,该类型的程序一般追求CPU实用效率最高,可使用Parallel Scavenge作为垃圾回收器,如果程序是web 应用程序,用户体验比较重要,可选用停顿时间比较短的CMS作为垃圾回收器。


本文参考博客: http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值