JVM(5)—GC垃圾回收
JVM(5)—GC垃圾回收
Java不需要C语言写析构函数手动释放对象。Java会自动对无用对象进行垃圾回收GC,释放占用的内存空间。
垃圾发现算法
- 什么条件下对象才算是垃圾?
当对象没有任何引用的时候,就代表对象无用了

引用链路断开,后续对象都会标记为垃圾。

- GC怎么知道对象没有引用?
- 引用计数算法
用计数器记录有多少个对象引用自己,当计数器值为0,表明没有被引用,代表是垃圾。

缺点:无法解决循环引用的情况。堆中对象互相引用。

- 可达性分析算法(根搜索算法)

从GC Root 出发,有引用的对象对象不可回收,没有引用的标记为垃圾后回收,是JVM默认算法。
GC Root 可以为:栈帧中的局部变量、静态变量、运行时常量池、JNI本地方法调用指针
垃圾回收算法
- 标记清除算法
先使用可达性分析算法标记,再把标记的清除。
内存不连续,碎片化。

- 复制算法
将空间分为两块大小相同的区域,先将存活对象从From区复制到To区, 再将From区的对象全部清除。
速度快,无碎片,但浪费了一半的存储空间。
年轻代Minor GC使用。

- 标记压缩算法
标记垃圾,清除垃圾,对可用对象进行压缩整理。
无碎片,但效率较低。
老年代Full GC使用。

- 分代收集算法
年轻代与老年代的特性决定。
年轻代空间小,用复制算法浪费的空间就也小。
年轻代对象存活率低,复制算法效率高。
老年代对象存活率高,空间大,所以用标记压缩算法
分代GC垃圾收集器
用来执行垃圾发现与垃圾回收算法。

- Serial收集器

-
年轻代收集器。
-
单线程GC,效率低。
-
适合单核CPU、小内存(几十mb)场景。
-
已淘汰。
- ParNew收集器

- 年轻代收集器。
- 多线程GC,效率不错。
- 优先降低SWT时间,但GC会多次。
- 适合多核CPU,n个G内存场景。
- 适合用户交互场景(用户点一下按钮等待时间少)。
- Parallel Scavenge(PS)收集器

- 年轻代收集器。
- 吞吐率优先考虑(运行用户代码时间/运行用户代码时间+GC垃圾回收时间),所以GC总时间少。
- STW时间较长,GC次数少。
- 多线程GC,效率不错。
- 适合多核CPU。
- 适合后台计算任务。
- 1.8默认年轻代GC收集器。(大多开发服务器,和用户交互不是很多,后台计算任务多)
- SerialOld收集器

- Serial收集器老年代版本
- 1.8默认老年代收集器
- 单线程GC,效率低
- 适合单核CPU
- 作为CMS备用收集器
- Parallel Old收集器

- PS收集器老年代版本
- 关注吞吐量
- STW时间较长,GC次数少。
- 多线程GC,效率不错。
- 适合多核CPU。
- 适合后台计算任务。
- CMS收集器

- 用户线程与GC并行执行,但用户线程CPU效率会降低
- 超短STW,适合几十G内存
- 会产生浮动垃圾
- 会产生标记失败
- 采用的是标记清除算法,产生大量内存碎片,当没空闲内存时会调用SerialOld进行Full GC(标记压缩算法),效率超低。
- JDK14被放弃
初始标记:只标记GCRoot第一层关联的对象。所以此STW时间极短。
浮动垃圾:用户线程和GC线程并行执行时,GC刚刚标记完垃圾后,用户又新产生了垃圾对象,此垃圾对象无法被标记。
标记失败:用户线程和GC线程并行执行时,标记为垃圾的对象又有用户进程对它进行了引用,此垃圾对象不能被回收。
重新标记:对浮动垃圾、标记失败的对象重新标记。扫描的数据量小,所以此STW时间短。
设置GC收集器的组合

# 查看当前GC收集器组合
java -XX:+PrintCommandLineFlags -version
# 设置GC收集器组合
java -jar -XX:UseSerialGC xxx.jar
不分代收集器
- G1收集器

- 1.9默认GC
- 多线程并发标记,并发回收
- 极短GC时间,单次STW默认最多200ms,有效时间内判断哪一块垃圾比较多,优先争对垃圾多的地方回收。
- 将堆切分无数小的内存区域(物理分区),根据逻辑分代(Eden、Old、Survivor),不物理隔离新生代与老年代
- 物理分区的大小只能为1/2/4/8/16/32mb
- 支持上百G内存
新生代回收Minor GC(复制算法),触发条件:新生代(Eden + Survivor)大小占堆大小的60%。
新生代+老年代回收Mixed GC,触发条件:老年代大小超过堆大小的45%。

初始标记STW:标记GCRoot与第一层的对象。
并发标记:与用户线程并发完成GCRoot引用链,但会产生浮动垃圾与标记失败。
最终标记STW:确认浮动垃圾与标记失败的垃圾。
标记压缩算法回收STW:G1开辟一块最多5%堆空间的内存用于标记压缩的数据交换,回收时在STW200ms内回收10%垃圾最多的区域,回收后检查老年代是否低于堆大小的45%,未达标就再来一遍,最多8遍,8遍还未达标就调用Serial Old进行Full GC。
- ZGC低延迟收集器
![]()

- 11出现的实验性GC,只在OracleJDK中使用
- 小于10ms的STW时间
- 也像G1使用分区,但不是固定容量,采取了动态容量,小-2MB,中-32MB,大-动态产生
- 支持T级内存GC
- 彻底不分代,利用“染色指针”实现对象引用。
- 当GC“标记压缩”回收对象时,对象内存指针会发生变化,但不需要STW,可以和用户线程并发运行。
- Shenandoah低延迟收集器

- 12出现的实验性GC,OpenJDK中使用
- 小于10ms的STW时间
- 彻底不分代,不同与ZGC动态块大小,Shenandoah使用固定块大小装对象
- 支持T级内存GC
- 利用读屏障与“转发指针”实现对象动态引用
- 当GC“标记压缩”回收对象时,对象内存指针会发生变化,但不需要STW,可以和用户线程并发运行。


浙公网安备 33010602011771号