深入理解垃圾回收机制及其实现原理

垃圾回收时机

1.System.gc()

显示调用System.gc():这个方法的调用是建议JVM进行FGC,只是建议,不是一定,但很多情况下它会触发FGC,从而增加FGC的频率

2.JVM垃圾回收机制决定

  • 创建对象是分配内存空间,如果空间不足,会触发GC
  • 其他回收机制

Object类的finalize(): 垃圾回收线程在回收垃圾之前,会先调用finalize()方法,设置一个可回收标志位,最后垃圾回收时,就把标记可回收的都回收掉,调用此方法不会造成对象的“死亡”

3.垃圾回收策略 ------ 如何判断对象已“死”

3.1 引用计数算法

给对象中添加一个引用计数器,只要引用,计数器就+1;引用失效时,计数器就-1;任何时刻计数器为0的对象就是不可能在被使用的。算法实现简单,判定效率高,但是很难解决对象之间相互循环引用的问题。

3.2 可达性分析算法

通过一系列的称为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为GC Roots引用链,当一个对象到GC Roots没有任何引用链相连时(从GC Roots到这个对象不可达),证明此对象是不可用的
下图表示虽然object5和object6相互引用,但是由于到GC Roots都不可达因此判定为可回收的对象
深入理解垃圾回收机制及其实现原理_第1张图片

4.Java的引用类型

Java将引用分为强引用、软引用、弱引用、虚引用4种,这4种引用强度依次逐渐减弱

4.1 强引用
Object obj = new Object();

这种类型的引用就叫强引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象

4.2 软引用

用来描述一些还有用但并非必需的对象
对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象进行第二次回收,如果这次回收后,还是没有足够的内存,才会抛出内存溢出异常

4.3 弱引用

用来描述非必需的对象,被弱引用关联的对象只能生存在下一次垃圾回收之前。无论当前内存是否足够,都会回收只被弱引用关联的对象

4.4 虚引用

称为幽灵引用或者幻影引用,最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对它的生存时间构成影响,也无法通过虚引用来取得一个对象实例,关联的目的是能在这个对象被收集器回收时收到一个系统通知
深入理解垃圾回收机制及其实现原理_第2张图片

5.需要垃圾回收的内存

5.1 方法区(JDK1.7) / 元空间(JDK1.8)
  • JDK1.7的方法区在GC中一般称为永久代
  • JDK1.8的元空间存在于本地内存,GC也是对元空间垃圾回收
  • 元空间或者永久代的垃圾回收主要回收两个部分内容:废弃常量和无用的类。这个区域进行垃圾回收的“性价比”比较低
5.2 堆
  • Java堆是垃圾回收器管理的主要区域,因此也叫GC堆
  • 从内存回收的角度看,垃圾回收基本都采用分代收集算法,Java堆还可以细分为:
    1.新生代: 又可以分为 Eden 空间、From Survivor 空间、To Survivor 空间
    新生代的垃圾回收又称为Young GC(YGC)、Minor GC
    指发生在新生代的垃圾收集动作,因为Java对象大多数都具备朝生夕灭的特性,所以YGC非常频繁,一般回收的速度也很快
    2.老年代:
    老年代垃圾回收又称为Major GC
    指发生在老年代的GC,出现了Major GC ,经常会伴随至少一次YGC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)Major GC的速度比YGC的速度慢10倍以上
    3.Full GC: (一般根据语义来定回收 的内存区域,一般都存在老年代GC )一般指影响很大(STW),所以一般都包含Major GC,但是当Minor GC时,新生代中Survivor空间不够,复制到老年代中,如果老年代空间还不够,这种情况就可能出现Minor GC
    深入理解垃圾回收机制及其实现原理_第3张图片
    方法调用产生大量的朝生夕灭的对象
    结果:在新生代产生大量的YGC
    (1)新生代很快空间不足,要进行YGC/Minor GC
    (2)回收的对象非常多:GC之后,新生代可用内存会很多
    (3)可能导致Major GC

6.垃圾回收算法

6.1 标记-清除算法(Mark-Sweep算法)
  • 最基础的算法,属于老年代回收算法
  • 首先标记出所有需要回收的对象,在标记完成后,统一回收所有标记的对象
  • 不足:
    1.效率:标记和清除两个过程的效率不高
    2.空间:标记清除后会产生大量的不连续的内存碎片,空间碎片太多会导致在程序运行过程中需要分配大对象时,无法找到足够的内容而提前触发下一次的垃圾回收
6.2 复制算法(Copying算法)
  • 属于新生代的回收算法
  • 将可用的划分为大小相等的两块,每次只使用其中的一块,当这一块的内存用完了,就将还存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉,实现简单,运行高效
  • 内存利用率只有50%
6.3 标记-整理算法(Mark-Compact算法)
  • 属于老年代的算法
  • 首先标记出所有需要回收的对象,移动存活对象到一端,清理另一端
6.4 分代收集算法
  • 当前JVM垃圾都采用的是"分代收集"算法,根据对象存活周期的不同 将内存划分为几块
  • 一般是把Java堆分为新生代和老年代
  • 把新生代内存分为一块较大的Eden(伊甸园)空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,Eden:From Survivor:To Survivor = 8:1:1,所以每次新生代的可用内存空间为整个新生代内存空间的90%,只有10%会被“浪费”
  • 新生代中,每次垃圾回收只有少量对象存活,因此采用复制算法,而老年代的存活率高,没有额外的空间对它进行分配担保,就采用标记-清除算法或者标记-整理算法

7.垃圾回收的过程

  • Eden空间不足,触发Minor GC :用户线程创建的对象优先分配在Eden区,当Eden区空间不够时,会触发Minor GC:将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间深入理解垃圾回收机制及其实现原理_第4张图片
  • 线程回归结束后,用户线程又开始新创建对象并分配在Eden区,当Eden区空间不足,又重复上述步骤进行Minor GC
  • 年老对象晋升到老年代: 虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并且经过一次Minor GC 后仍然存活,并且能被 Survivor 容纳的话,将被移动到 Survivor 空间中,并且把对象年龄设为1.对象在Survivor空间中,每熬过一次 Minor GC ,年龄就增加1岁,当它的年龄增加到一定程度(默认为15岁),就将晋升到老年代
  • Survivor空间不足,存活对象通过分配担保机制进入老年代

空间担保机制
发生新生代GC时,是使用复制算法将Eden区和Survivor区存活的对象复制到另一个Survivor区

  • Survivor区只占用新生代10%的内存,当Survivor空间不够用时,需要依赖其他内存(老年代)进行分配担保

9.垃圾收集的影响

9.1用户线程的暂停:Stop-The-World(STW)

GC导致的暂停用户线程:
原因:用线程和GC线程并发、并行执行,标记可回收对象,在用户线程并发执行时,可能重新有引用指向该对象

新生代GC都会STW,影响:因为Minor GC时间非常快,影响很小;
老年代GC会根据阶段来看是否会STW,影响非常大(老年代空间大,需要回收的对象多,耗时也多)

垃圾回收工作是在垃圾回线程中执行的,在很多情况下,执行垃圾回收工作或是执行垃圾回收其中某一个步骤时,需要暂停线程,也就是 Stop-The-World

  • 垃圾回收工作首先是需要标记的,对象被标记后,会根据区域的不同采用不同的回收方法
  • GC发生时让所有线程暂停,即:让所有线程跑到最近的安全点上在停顿下来,
    1.抢先式中断: 不需要线程的执行代码主动去配合,在GC发生时,把所有线程全中断,如果发现有线程没在安全点上,就恢复线程,让它跑到安全点上
    2.主动式中断: 当GC需要中断线程的时候,设置一个标志,各个线程执行时,主动去轮询这个标志,发现中断标志为真时就自己中断挂起,轮询标志的地方、安全点、创建对象需要分配内存的地方是重合的
9.2 垃圾回收器的指标(吞吐量和停顿时间)
  • 并行:指多条垃圾收集线程并行工作,用户线程处于等待状态
  • 并发:指用户线程与垃圾收集线程同时执行(不一定并行,可能会交替执行),用户程序继续运行,而垃圾收集程序在另一个CPU上

吞吐量: 指标和STW总时间有关,越长吞吐量越低,越短吞吐量越高
用户体验优先: 用户线程单次停顿时间短,即使总的停顿时间长一点也可以接受
吞吐量优先: 用户线程总的停顿时间短,即使单次停顿时间长一点也可以接受

10.垃圾回收(GC)、用户线程、垃圾回收线程、垃圾回收算法、垃圾收集器之间的关系

垃圾收集器是做垃圾回收工作的任务,JVM提供了很多的具体实现,针对不同区域有多种实现具体的垃圾收集器,可能采取不同的垃圾回收算法
深入理解垃圾回收机制及其实现原理_第5张图片

11.垃圾收集器

虚拟机提供了7种不同分代的收集器,连线说明可以搭配使用
深入理解垃圾回收机制及其实现原理_第6张图片

11.1 Serial收集器(新生代收集器,串行GC)

特性:
1.单线程
2.复制算法
3.Stop The World

单个CPU的环境来说,Serial收集器没有线程交互的开销,收集效率较好

串行:不能并发执行,只能收集完一个,在收集下一个

11.2 ParNew收集器(新生代收集器,并行GC)

特性:
1.多线程
2.复制算法
3.Stop The World

搭配CMS收集器,在用户体验优先的程序中使用-----> 用户要调用程序接口,执行程序代码

相比单CPU环境,随着CPU的增加,它对于GC时系统资源的有效利用还是有好处的

并行:需要暂停用户线程,多线程

11.3 Parallel Scavenge(新生代收集器,并行GC)

特性:
1.多线程
2.复制算法
3.可控制吞吐量:提供了两个参数,分别是最大垃圾收集停顿时间、吞吐量大小
4.自适应的调节策略: 将这个参数设置为true,就不需要手工指定参数,虚拟机会监控性能,动态调整参数(新生代的大小、Eden区与Survivor区的比例、晋升老年代的年龄等)
5.吞吐量优先-----> 主要是后台任务型程序(不涉及用户使用的程序)

11.4 Serial Old收集器(老年代收集器,串行GC)

特性:
1.单线程
2.标记-整理算法

作为CMS收集器的备选方案,在发生并发失败时(Concurrent Mode Failure)作为备用垃圾回收

11.5 Parallel Old(老年代收集器,并行GC)

特性:
1.多线程
2.标记-整理算法
3.吞吐量优先

11.6 CMS收集器(老年代收集器,并行GC)

特性:
1.并发收集,低停顿
2.标记-清除算法
3.分为4步

  • (1).初始标记:标记GC Roots能直接关联到的对象,速度很快,需要STW
  • (2).并发标记:追踪第一步标记的对象所关联的对象,GC Roots Traing ,和用户线程可以一起执行
  • (3).重新标记:解决第二个阶段,用户线程并发执行,导致已经标记的对象(可回收),被重新引用(有GC Roots 变量指向该对象) 需要STW
  • (4).并发清除:并发清除对象

整个过程中,耗时最长的并发标记和并发清除过程收集器都可以和用户线程一起工作,所以总体看来 ,CMS内存回收过程是与用户线程并发执行

适用于用户体验优先

缺陷:
1.CMS会抢占CPU资源。需要CPU分配多条执行垃圾收集线程,从而使用户线程的执行速度较慢
2.CMS无法处理浮动垃圾,可能会出现Concurrent Mode Failure而导致另一次Full GC

在并发清除的过程中,用户线程还在执行,会继续产生垃圾,这些新的垃圾没有被标记,CMS只能在下一次集中处理,这也导致了CMS不能在老年代几乎完全被填满了再去收集,必须预留一部分空间提供给并发收集时程序运作使用
如果CMS在收集期间,内存无法满足程序的需要,就会出现Concurrent Mode Failure ,这是JVM将启动Plan B,也就是临时调用单线程的Serial Old 收集器来重新进行垃圾回收
深入理解垃圾回收机制及其实现原理_第7张图片
深入理解垃圾回收机制及其实现原理_第8张图片

3.因为使用标记清除算法,所以会产生大量的空间碎片,如果进入的对象没有连续的空间,就会触发Full GC

CMS提供了一个开关参数(默认是开启的),用于在CMS收集器进行Full GC时对内存碎片进行合并整理,过程是需要暂停用户线程,碎片虽然没有了,但是停顿时间变长了
CMS还有一个参数是用于设置执行多少次不压缩的Full GC后,再来一次带压缩的Full GC

11.7 G1收集器(全区域的垃圾回收器)
  • 首先将堆划分为许多region块,每个region动态指定为Eden区,Survivor区和老年代,然后并行对它们进行垃圾回收
  • G1垃圾回收器回收region块的时候基本不用STW,而是基于(整体看来是标记整理算法,从局部即两个region之间基于复制算法)的策略来对region进行垃圾回收
  • 用户体验优先
  • 无论如何,G1收集器采用的算法都意味着 一个region有可能属于Eden,Survivor或者老年代内存区域
  • G1垃圾回收器在清除实例所占用的内存空间后,还会进行内存压缩

对于老年代的垃圾收集,G1也分为4个阶段:

  • 初始标记:STW,从跟对象出发,在跟对象的第一层孩子节点中标记所有可达的对象,G1收集器的第一个阶段是可以和新生代GC 同时执行
  • 并发标记:由于老年代中对象的存活率很小或者基本没有存活,那么G1将会在这个阶段将其回收掉,G1也会计算每个region的对象存活率
  • 最终标记:STW,和第三个阶段算法不同
  • 筛选回收:G1会挑选出那些对象存活率低的region进行回收,也可以和新生代GC同时发生---->clean up / copy

你可能感兴趣的:(编程)