目录
[1. 什么是垃圾](#1. 什么是垃圾)
[2. Java垃圾回收机制](#2. Java垃圾回收机制)
[1. 垃圾标记算法](#1. 垃圾标记算法)
[1.1 引用计数算法](#1.1 引用计数算法)
[1.2 可达性分析算法](#1.2 可达性分析算法)
[2. 对象的finalization机制](#2. 对象的finalization机制)
[3. 清除阶段](#3. 清除阶段)
[3.1 标记-清除算法(Mark-Sweep)](#3.1 标记-清除算法(Mark-Sweep))
[3.2 复制算法](#3.2 复制算法)
[3.3 标记-压缩算法](#3.3 标记-压缩算法)
[4. 分代收集算法](#4. 分代收集算法)
[5. 增量收集算法](#5. 增量收集算法)
[6. 分区算法](#6. 分区算法)
[1. System.gc()](#1. System.gc())
[2. 内存溢出](#2. 内存溢出)
[4. Stop The World](#4. Stop The World)
[5. 安全点与安全区域](#5. 安全点与安全区域)
[6. 四种引用](#6. 四种引用)
[6.1 软引用](#6.1 软引用)
[6.2 弱引用](#6.2 弱引用)
[6.3 虚引用](#6.3 虚引用)
[1. GC的分类和性能指标](#1. GC的分类和性能指标)
[1.1 分类](#1.1 分类)
[1.2 性能指标](#1.2 性能指标)
[2. 不同的垃圾回收器](#2. 不同的垃圾回收器)
[3. Serial回收器:串行回收](#3. Serial回收器:串行回收)
[4. ParNew回收器:并行回收](#4. ParNew回收器:并行回收)
[5. Parallel回收器:吞吐量优先](#5. Parallel回收器:吞吐量优先)
[6. CMS回收器:低延迟](#6. CMS回收器:低延迟)
[7. G1垃圾回收器:区域化分代](#7. G1垃圾回收器:区域化分代)
[7.1 优势与不足](#7.1 优势与不足)
[7.2 适用场景](#7.2 适用场景)
[7.3 Region的介绍](#7.3 Region的介绍)
[7.4 记忆集](#7.4 记忆集)
[7.5 G1回收器垃圾回收过程](#7.5 G1回收器垃圾回收过程)
[7.5.1 年轻代GC](#7.5.1 年轻代GC)
[7.5.2 并发标记过程](#7.5.2 并发标记过程)
[7.5.3 混合回收](#7.5.3 混合回收)
[8. 总结](#8. 总结)
一、垃圾回收概述
1. 什么是垃圾
垃圾是指在运行程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾。


2. Java垃圾回收机制


二、垃圾回收相关算法
1. 垃圾标记算法

1.1 引用计数算法


1.2 可达性分析算法
该算法可以有效解决在引用计数算法中循环引用的问题,防止内存泄露的发生。
"GC Roots"根集合就是一组必须活跃的引用。

如下图所示:

在Java语言中,GC Roots包括以下几类元素:
- 虚拟机栈中引用的对象
- 本地方法栈中引用的对象
- 类静态属性引用的对象
- 常量引用的对象(例如字符串常量池中的引用)
- 所有被同步锁synchronized所持有的对象
- Java虚拟机内部的引用

2. 对象的finalization机制

不要主动去调用某个对象的finalize()方法,应该交给垃圾回收机制调用。
由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态:
- 可触及的:从GC Roots根结点开始,可以到达的对象。
- 可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活。
- 不可触及的:对象的finalize()被调用,并且没有复活,那么就会进入不可触及状态。
只有对象在不可触及状态的时候才可以被回收。

如果想要第一次被标记的对象复活,就需要重写finalize()方法。
3. 清除阶段
当成功区分出内存中存活对象和死亡对象之后,GC接下来的任务就是执行垃圾回收,释放掉无用对象所占用的空间。
3.1 标记-清除算法(Mark-Sweep)

优点:简单、方便。
缺点:
- 效率不算高
- 在进行GC的时候需要停止整个应用程序
- 这种方式清理出来的空闲空间内存是不连续的,会产生内存碎片。需要维护一个空闲列表

3.2 复制算法

如下图所示:
A和B是两块内存空间,要对A进行垃圾回收的话,就将标记的对象复制到内存B中,然后清空内存A。之后放数据往内存B中放,若B要进行垃圾回收,则再往A中复制。

优点:
- 没有清除过程,实现简单,运行高效
- 复制过去后保证空间的连续性,不会出现"碎片"问题
缺点:
- 内存空间始终有一半是空闲状态
- 存活对象如果过多,那么时间开销和资源开销就会很大
3.3 标记-压缩算法

优点:
- 消除了标记-清除算法中内存分散的缺点,当需要给新对象分配内存的时候,JVM只需要持有一个内存的起始地址即可。
- 消除了复制算法中内存减半的高额代价。
缺点:
- 效率较低
- 移动对象的同时,如果对象被引用,还需要修改引用的地址
- 移动过程中,需要全程暂停用户应用程序。即:STW
4. 分代收集算法
分代收集算法并不是一种具体的算法,而是一种思想。
不同对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。一般是把Java堆分为新生代和老年代,这样就可以根据各个区域的特点使用不同的回收算法。
目前几乎所有的GC都是采用分代收集算法执行垃圾回收的。

5. 增量收集算法
为了解决STW状态,诞生了增量收集算法。

缺点:使用这种方法,虽然能减少系统的停顿时间。但是,因为线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。
6. 分区算法

如果设置的停顿时间短,那么就少回收几个region。如果停顿时间长,就多回收几个region。
三、垃圾回收相关概念
1. System.gc()

就是说,就算在程序中调用了System.gc()方法,该方法也不一定会调用垃圾回收器来对垃圾进行回收。
2. 内存溢出
内存溢出指的就是OOM异常,没有空闲内存,并且垃圾收集器也无法提供更多内存。



3.内存泄漏
严格来说,只有当对象不会再被程序用到了,但是GC又不能回收他们的情况,才叫内存泄露。
尽管内存泄露不会立刻引起程序崩溃,但是一旦发生内存泄露,程序中的可用内存就会被逐步蚕食,直到耗尽所有内存,最终出现OOM异常。

例如创建了许多static的变量,生命周期和类一样长,即便之后不用了,也无法被回收,导致内存泄漏。

上图的例子:红框框出来的对象都不再使用了,但是一个忘记断开的引用也会导致内存泄漏。

4. Stop The World


5. 安全点与安全区域
程序在执行过程中并非在所有地方都能停下来开始GC,只有在特定的位置才能停顿下来开始GC,这些位置称为"安全点"。

安全区域是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的。我们也可以把安全区域看作是被扩展的安全点。

6. 四种引用

6.1 软引用
软引用通常来实现内存敏感的缓存。例如高速缓存就用到软引用。第二次回收指的是在回收完那些不可达的对象之后内存还是不够,才进行第二次回收。

6.2 弱引用
弱引用也是用来描述那些非必须的对象,被弱引用关联的对象只能生存到下一次垃圾收集发生为止。

6.3 虚引用
虚引用不能单独使用,也无法通过虚引用来获取被引用的对象。当试图通过虚引用的get()方法获取对象时,总是null。


四、垃圾回收器
1. GC的分类和性能指标
1.1 分类
按线程数分,可以分为串行垃圾回收器 和并行垃圾回收器。

串行回收指的是在同一时间段内只允许有一个CPU用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾回收工作结束。
并行收集可以运用多个CPU同时执行垃圾回收,因此提升了吞吐量,不过并行回收和串行回收一样,采用独占式,使用了STW机制。
按照工作模式分,可以分为并发式垃圾回收 和独占式垃圾回收。

按碎片处理方式分,可以分为压缩式垃圾回收器 和非压缩式垃圾回收器。
按工作的内存空间分,可分为年轻代垃圾回收器 和老年代垃圾回收器。
1.2 性能指标



2. 不同的垃圾回收器
7种经典的垃圾回收器:


一个新生代的垃圾回收器需要组合一个老年代的垃圾回收器,组合关系如下。红色虚线表示JDK8之前的关系,绿色虚线在JDK14时弃用了**。CMS垃圾回收器在JDK14也删除掉了**。

3. Serial回收器:串行回收


4. ParNew回收器:并行回收
ParNew收集器除了采用并行回收 的方式执行内存回收外,它与Serial回收器几乎没有任何区别。PerNew收集器在年轻代中同样也是采用复制算法、"Stop-the-World"机制。
ParNew是很多JVM运行在Server模式下新生代的默认垃圾收集器。

5. Parallel回收器:吞吐量优先



6. CMS回收器:低延迟
CMS采用标记-清除算法,第一次实现了让垃圾回收线程与用户线程同时进行工作。

- 初始标记阶段 :程序中所有的工作线程因为STW机制而出现短暂的暂停,这个阶段的主要任务仅仅只是标记出GC Roots能直接关联到的对象。一旦标记完成之后就会恢复所有的线程。这里的速度非常快。
- 并发标记阶段 :从GC Roots的能直接关联对象开始遍历整个对象图的过程,这个过程耗时较长但是不需要停顿用户线程。
- 重新标记阶段 :由于在并发标记过程中,程序的工作线程和垃圾回收线程并发执行,因此为了修正并发标记期间,因为用户程序运行而导致标记产生变动的那一部分对象,这个阶段时间稍长,但也比第二阶段短的多。
- 并发清除阶段 :此阶段清理掉的标记阶段判断的已经死亡的对象,释放空间。


CMS优点:
- 并发收集
- 低延迟
CMS缺点:
- 会产生内存碎片
- CMS收集器对CPU资源非常敏感
- CMS收集器无法处理浮动垃圾

7. G1垃圾回收器:区域化分代
G1是一个并行回收器,它把堆内存分割为很多不相关的区域。使用不同的Region来表示Eden、Survivor0、Survivor1和老年代。
G1跟踪各个Region里面的垃圾堆积的价值大小,在后台维护一个优先列表 ,每次根据允许的收集时间,优先回收价值最大的Region。
7.1 优势与不足
优势:
- G1垃圾回收器兼顾并行与并发。
- 将堆空间分为多个Region,既可以回收新生代,也可以回收老年代。
- 空间整合:Region之间是复制算法,整体上看是标记-压缩算法。GC之后会将现有的资源放到一起,不会产生内存碎片。
- 可预测的停顿时间模型:后台维护了一个优先列表,每次根据允许的收集时间,优先回收价值最高的最大的Region,保证G1收集器在有限时间内可以获取尽可能高的收集效率。

缺点:

7.2 适用场景


7.3 Region的介绍



7.4 记忆集
一个Region中的对象可能被其他任意Region中的对象所引用,判断对象是否存活,需要扫描整个Java堆才能保证准确。
无论是G1还是其他分代收集器,JVM都是使用Rset来避免全局扫描。即每一个Region都有一个对应的Rset,Rset中记录了有哪些区域的对象引用了该Region中的对象。
如下图所示:

7.5 G1回收器垃圾回收过程
G1的垃圾回收过程主要包括三个环节:年轻代GC、老年代并发标记过程、混合回收。
如果需要,Full GC还是继续存在的。它针对GC的评估失败提供了一种失败保护机制,即强力回收。


7.5.1 年轻代GC

7.5.2 并发标记过程

首先标记GC Roots直接可达的对象 --> 标记survivor区中可以直接到老年代的对象 --> 对整个堆进行标记(并发) --> 再次标记 --> 计算各个区域的GC回收比例 --> 并发清理。
7.5.3 混合回收

第二阶段老年代Region中全是垃圾的被回收了,部分为垃圾的在第三阶段--混合回收的时候被回收。
8. 总结
