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收集。 |
6,总结
众多的垃圾回收器,没有最好的,只有最适合的,如果程序是计算型的应用程序,该类型的程序一般追求CPU实用效率最高,可使用Parallel Scavenge作为垃圾回收器,如果程序是web 应用程序,用户体验比较重要,可选用停顿时间比较短的CMS作为垃圾回收器。
本文参考博客: http://www.cnblogs.com/hnrainll/archive/2013/11/06/3410042.html