JAVA虚拟机精讲-第六章 内存分配和垃圾回收(二)

0x00 GC

上一篇文章讲了jvm的内存,这次主要讲第六章的剩余部分,垃圾回收。

GC(Garbage Collector)是垃圾收集器。由于java是自动分配内存,所以需要jvm自动回收超过作用域的内存

经常也代表 垃圾回收(Garbage Collection,GC)的主要作用是回收程序中不再使用的内存

0x01 GC算法

  • 引用计数算法:引用和去引用伴随加法和减法,影响性能。致命的缺陷:对于循环引用的对象无法进行回收(已淘汰)
  • 根搜索算法:设立若干种根对象,当任何一个根对象到某一个对象均不可达时,则认为这个对象是可以被回收的。现代垃圾搜集的算法主要有三种,分别是标记-清除算法、复制算法、标记-整理算法。这三种算法都扩充了根搜索算法。

标记-清除算法: 标记-清除算法是现代垃圾回收算法的思想基础。标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;然后,在清除阶段,清除所有未被标记的对象。

缺点:1.效率比较低(递归与全堆对象遍历),导致stop the world的时间比较长。2.空闲内存是不连续的

复制算法:(新生代的GC):将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

缺点:空间的浪费

标记-整理算法:(老年代的GC):标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。

缺点效率也不高

 

根对象(GC Roots):

  • Java栈中的对象引用;
  • 运行时常量池的对象引用;
  • 方法去中类静态属性的对象引用;
  • 与一个类对应的唯一数据类型的Class对象。

0x03 分代收集:就是新生代和老年代的区别

新生代:初始对象,生命周期短的。 永久代(老年代):长时间存在的对象

  • Minor Gc通常发生在新生代的Eden区,在这个区的对象生存期短,往往发生Gc的频率较高,回收速度比较快
  • Full Gc/Major GC 发生在老年代,一般情况下,触发老年代GC的时候不会触发Minor GC,但是通过配置,可以在Full GC之前进行一次Minor GC这样可以加快老年代的回收速度

minor-gc-major-gc-full-gc

0x04 Java的垃圾回收器(HotSpot)

Serial

使用拷贝算法的垃圾回收器,只能用于新生代。Serial回收器使用单线程进行垃圾回收。

SerialOld

使用标记-压缩算法的垃圾回收器,只能用于老年代。使用单线程进行垃圾回收。

Serial SerialOld回收器的组合是JVM最基础的垃圾回收器组合,只使用单CPU,STW(Stop-The-World)时间较长,适用于处理能力不强的主机和对STW时长要求不高的软件,JVM如以client模式启动,则默认使用Serial SerialOld回收器。
在启动参数中指定-XX: UseSerialGC,会启用Serial SerialOld组合

Serial和SerialOld回收器适用于只有单核CPU的运行环境,效率比较低,一般情况下,只用于开发环境或桌面程序

ParNew

Serial回收器的多线程版本,只能用于新生代。使用拷贝算法,多线程并行工作。在多CPU主机上的性能高于Serial,单CPU主机上的性能低于Serial。

Parallel Scavenge

与ParNew一样,都是用于新生代的并行拷贝算法回收器。区别在于Parallel Scavenge回收器可以控制新生代垃圾回收的STW时间。

Parallel Old

Parallel Scavenge的老年代版本,于JDK1.6中面世。在Parallel Old诞生之前,Parallel Scavenge处于一个比较尴尬的境地,由于没有对应的老年代回收器与其配合,仅在新生代使用Parallel算法很难达到对整体垃圾回收时长和STW时长的控制目的。
而现在,我们可以使用Parallel Scavenge Parallel Old这一组合来解决这一问题

Concurrent Mark Sweep(CMS)

CMS是标记-清除的改进算法,用于老年代,能够有效减少STW时长。
CMS是一种比较复杂的垃圾回收算法,此处尽可能进行简明扼要的介绍:
CMS将标记-清除细分为6个阶段:

  • 初始标记
  • 并发标记
  • 并发预清理
  • 重新标记
  • 并发清理
  • 并发重置

详细过程:

  1. 初始标记阶段中,CMS回收器标记出被GC Roots直接引用的对象,这一过程需要STW,但由于只进行深度为1的遍历,耗时很短。
  2. 并发标记阶段中,CMS回收器以初始标记阶段标记出的存活对象为根进行可达性遍历。在这一阶段中,不需要STW,其他线程可正常运行。
  3. 并发预清理阶段中,CMS回收器对并发标记阶段中老年代新增的对象重新进行标记。这一阶段存在的目的是尽可能减少下一阶段“重新标记”的STW时长。
  4. 重新标记阶段会进入STW,然后进行一次完整的可达性分析,由于前面三个阶段已经完成了绝大部分的工作,所以这一阶段的STW会很短。
  5. 并发清理阶段不需要STW,垃圾回收线程清理标记出的垃圾对象,同时其他线程可以正常工作。
  6. 并发重置阶段中,重置CMS回收器的数据结构,等待下一次垃圾回收。

可以看出来,CMS回收器的思路是把标记-清除算法的工作拆分成多个步骤,其中可以并行的尽可能并行,以达到STW时长最小化的目标。

Garbage-First(G1)

G1回收器诞生于Hotspot VM的7update4版本,这一最新型的垃圾回收器吸取了CMS回收器的经验和教训,旨在解决CMS回收器的各类弊端,同时提供更短更可控的STW时长。

G1的机制比CMS更加复杂,此处同样尽可能简明的进行介绍:

与其他回收器不同,G1是一个全代回收器,同时负责新生代和老年代的垃圾回收工作。
G1回收器打破了Hotspot VM以往的分代概念,新生代的Eden、S0、S1,以及老年代不再是物理分隔,而变成了灵活的逻辑分隔。G1将堆内存划分为2000个左右相等的内存块,每个内存块的大小为1-32Mb。每个内存块可以作为Eden、S0、S1或老年代使用,也就是说这些块的身份是不固定的。随着每次垃圾回收的完成,有些块的内存会被完全释放掉,成为空白块,而这些空白块在接下来可能成为任何一种角色。

[…]

粤ICP备17041560号-2