JVM-GC算法与种类

一、基础概念

根节点:
1、栈中引用的对象;
2、方法区中静态成员或者常量引用的对象(全局对象);
3、JNI方法栈中引用对象;

可达性分析(Reachability Analysis):从根节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到根节点有引用链相连时,则证明此对象是可达对象;

可复活对象:在finalize()方法中复活的对象;

不可达对象:既没有引用链又不可复活的对象,称之为不可达对象。不可达对象是要被GC回收的对象;

Minor GC:从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC;

Major GC: 清理永久代垃圾;

Full GC: 是清理整个堆空间—包括年轻代、永久代、老年代

二、GC的概念

GC:Garbage Collection 垃圾收集,垃圾是指系统在运行期间产生的一些无用对象,若这些对象长时间不被释放,则会导致系统内存溢出。
java中,GC的对象时堆空间和永久区。

三、GC的算法

1、引用计数法
引用计数法是老的垃圾回收算法,它的实现很简单,在每一个对象上标记一个引用计数器,假设有一个对象A,只要有任何一个对象引用了A,则A的引用计数器就会+1,当引用失效时,引用计数器就会-1,当引用计数器为0时,则表示A对象不被引用了,将会被垃圾回收。

例如:
这里写图片描述

当对象C与对象B断开引用时,C的引用计数器既为0,C也就会被垃圾回收

引用计数法的问题
1、引用和去引用伴随着引用计数器的加法和减法,影响性能;
2、很难处理循环应用

这里写图片描述

当跟对象与其他对象断开引用时,那么其他对象对于跟对象来说是不可达的对象了,但是其他三个对象在循环引用,所以引用计数器不会为0,则不会被当做垃圾回收。

2、标记-清除算法
标记-清除算法是现代垃圾回收算法的思想基础,它将垃圾回收分为两个阶段,即标记阶段和清除阶段。
在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象,因此未被标记的即是未被引用的垃圾对象,在清除阶段就清除这些未被标记的对象。
缺点:
1、暂停整个应用;
2、会产生内存碎片;
这里写图片描述

3、标记-压缩算法
标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。

这里写图片描述

4、复制算法
复制算法是将原有的内存空间非为两块,每次只使用其中一块,在垃圾回收时,将正在使用的存活对象,复制到未被使用的内存块中去,然后将正在使用的内存块中的对象全部清除,交换两个内存块的角色,完成垃圾回收。
与标记-清除算法相比,复制算法是一种相对高效的回收方法
不适用于存活对象较多的场合 如老年代
缺点:
1、暂停整个应用;
2、需要2倍的内存空间;

这里写图片描述

这里写图片描述

老年代:存放从年轻代(young)复制过来的对象。生命周期较长的对象,归入到tenured generation。一般是经过多次minor gc,还依旧存活的对象,将移入到tenured generation。(当然,在minor gc中如果存活的对象的超过survivor的容量,放不下的对象会直接移入到tenured generation)。
持久代(perm):用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著的影响,但是有些应用可能动态生成或者调用一些class。

四、分代思想

依据对象的存活周期进行分类,短命对象归为新生代,长命对象归为老年代。每次GC存活的对象,年龄上都+1。
根据不同代的特点,选取合适的收集算法
少量对象存活,适合复制算法
大量对象存活,适合标记清理或者标记压缩

五、JVM的垃圾回收过程

首先从跟对象开始进行可达性分析,判断哪些是不可达对象。
对于不可达对象,判断是否需要执行其finalize方法,如果对象没有覆盖finalize方法或已经执行过finalize方法则视为不需要执行,进行回收;如果需要,则把对象加入F-Queue队列。
对于F-Queue队列里的对象,稍后虚拟机会自动建立一个低优先级的线程去触发其finalize方法,但不会等待这个方法返回。
如果在finalize方法的执行过程中,对象重新被引用,那么进行第二次标记时将被移出F-Queue,在finalize方法执行完成后,对象仍然没有被引用,则进行回收。
对于被移出F-Queue的对象,如果它下一次面临回收时,将不会再执行其finalize方法。
finalize方法只执行一次。

六、导致Minor GC的情况:

当Eden区域分配内存时,发现空间不足,JVM就会触发Minor GC,程序中System.gc()也可以来触发。

七、导致Full GC的情况:

除直接调用System.gc外,触发Full GC执行的情况有如下四种:

1. 老年代空间不足
老年代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误:java.lang.OutOfMemoryError: Java heap space
为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

2. 持久代空间满
PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息:java.lang.OutOfMemoryError: PermGen space
为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

3. CMS GC时出现promotion failed和concurrent mode failure
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入老年代,而此时老年代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的。

应对措施为:增大survivorspace、老年代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

4. 统计得到的Minor GC晋升到老年代的平均大小大于旧生代的剩余空间
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到老年代导致老年代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到老年代的平均大小大于老年代的剩余空间,那么就直接触发Full GC。
例如程序第一次触发MinorGC后,有6MB的对象晋升到老年代,那么当下一次Minor GC发生时,首先检查老年代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。
当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时老年代的剩余空间是否大于6MB,如小于,则触发对老年代的回收。。

八、关于finalize方法的问题

finalize方法使得GC过程做了更多的事情,增加的GC的负担。
如果某个对象的finalize方法运行时间过长,它会使得其他对象的finalize方法被延迟执行。
finalize方法中如果创建了strong reference引用了其他对象,这会阻止此对象被GC。
finalize方法有可能以不可确定的顺序执行(也就是说要在安全性要求严格的场景中尽量避免使用finalize方法)。
不确保finalize方法会被及时调用,也许程序都退出了,但是finalize方法还没被调用。
可以使用try-catch-finally来替代它

九、Stop-The-World

Java中一种全局暂停的现象
全局停顿,所有Java代码停止,native代码可以执行,但不能和JVM交互

引起原因
1、多半由于GC引起
2、Dump线程
3、死锁检查
4、堆Dump

GC时为什么会有全局停顿?
类比在聚会时打扫房间,聚会时很乱,又有新的垃圾产生,房间永远打扫不干净,只有让大家停止活动了,才能将房间打扫干净。
危害
长时间服务停止,没有响应
遇到HA系统,可能引起主备切换,严重危害生产环境。

参考文章:
http://itindex.net/detail/47030-cms-gc-问题

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值