关于后端:深入浅出JVM十二之垃圾回收算法

4次阅读

共计 2482 个字符,预计需要花费 7 分钟才能阅读完成。

上篇文章深入浅出 JVM(十一)之如何判断对象“已死”曾经深入浅出的解析 JVM 是如何评判对象不再应用,不再应用的对象将变成“垃圾”,期待回收

垃圾回收算法有多种,实用于不同的场景,不同的垃圾收集器应用不同的算法

本篇文章将围绕垃圾回收算法,深入浅出的解析垃圾回收分类以及各种垃圾回收算法

垃圾回收算法

垃圾回收分类

垃圾收集器有着多种 GC 形式,不同的 GC 形式有本人的特点,回收的堆内存局部也不同

堆内存分为新生代和老年代,新生代存储“年老”的对象,老年代存储“老”或内存大的对象,对象年龄由经验多少次 GC 来判断

其中整堆收集时不仅会回收整个堆还会回收元空间(间接内存)

  • 局部收集(Partial GC): 收集指标是局部空间

    • 新生代收集(Minor GC/ Young GC):收集新生代 Eden、Survive to、Survive from 区
    • 老年代收集(MajorGC/Old GC):收集老年代
    • 混合收集(Mixed GC):收集整个新生代和局部老年代
  • 整堆收集(Full GC): 整个堆 + 元空间

标记 - 革除算法

Mark-Sweep

标记:从 GCRoots 开始遍历援用链,标记所有可达对象 (在对象头中标记)

革除:从堆内存开始线性遍历,发现某个对象没被标记时对它进行回收

标记 - 革除算法实现简略、不须要扭转援用地址,然而须要两次遍历扫描效率不高,并且会呈现内存碎片

留神:这里的革除并不是真正意义上的回收内存,只是更新闲暇列表(标记这块内存地址为闲暇,后续有新对象须要应用就笼罩)

复制算法

Copying

Survive 辨别为两块容量一样的 Survive to 区和 Survive from 区

每次 GC 将 Eden 区和 Survive from 区存活的对象放入 Survive to 区,此时 Survive to 区改名为 Survive from 区,原来的 Survive from 区改名为 Survive to 区 保障 Survive to 区总是闲暇的

如果 Survive from 区的对象通过肯定次数的 GC 后(默认 15 次),把它放入老年代

流程图

留神:图中的 dean 为 Eden 区(写错)

留神:最终的 survive from 区存活对象占用的内存应该是(那两块蓝色)挨着一起的,图中为了标识字离开来了

复制算法不须要遍历,并且不会产生内存碎片,然而会节约 survive 区一半的内存,挪动对象时须要 STW 暂停用户线程,并且复制后会扭转援用地址(hotspot 应用间接指针拜访,还要扭转栈中 reference 执行新援用地址)

如果复制算法中对象存活率太高会导致非常耗费资源,因而个别只有新生代才应用复制算法

标记 - 整顿算法

Mark-Compact

标记:从 GCRoots 开始遍历援用链,标记所有可达对象(在对象头中标记)(与标记 - 革除算法统一)

整顿:让所有存活对象往内存空间一端挪动,而后间接清理掉边界以外的所有内存

标记 - 整顿算法不会呈现内存碎片也不会节约空间,然而效率低(比标记 - 革除还多了整顿性能),挪动对象导致 STW 和扭转 reference 指向

如果不挪动对象会产生内存碎片,内存碎片过多,将无奈为大对象分配内存

还有种办法: 屡次标记 - 革除,等内存碎片多了再进行标记 - 整顿

分代收集算法

标记革除 mark-sweep 复制 copying 标记整顿 mark-compact
速度
GC 后是否须要挪动对象 不挪动对象 挪动对象 挪动对象
GC 后是否存在内存碎片 存在内存碎片 不存在内存碎片 不存在内存碎片

须要挪动对象 意味着 要扭转改对象援用地址 也就是说要扭转栈中 reference 指向改对象的援用地址,并且会产生 STW 进展用户线程

当空间中存在大量内存碎片时,可能导致大对象无奈存储

分代收集算法 : 看待不同生命周期的对象能够采纳不同的回收算法(不同场景采纳不同算法)

年老代: 对象生命周期短、存活率低、回收频繁,采纳复制算法,高效

老年代: 对象生命周期长、存活率高、回收没年老代频繁,采纳标记 - 革除 或混用 标记 - 整顿

增量收集算法

mark-sweep、copying、mark-compact 算法都存在 STW,如果垃圾回收工夫很长,会重大影响用户线程的响应

增量收集算法: 采纳用户线程与垃圾收集线程交替执行

增量收集算法可能进步用户线程的响应工夫,但存在 GC、用户线程切换的开销,升高了吞吐量,GC 老本变大

分区算法

堆空间越大,GC 工夫就会越长,用户线程响应就越慢

分区算法: 将堆空间划分为间断不同的区,依据要求的进展工夫正当回收 n 个区,而不是一下回收整个堆

每个区独立应用,独立回收,依据能接受的进展工夫管制一次回收多少个区

G1 收集器以及两块低提早收集器 Shenandoah、ZGC 就应用到这种分区算法

总结

本篇文章围绕垃圾回收算法,深入浅出解析垃圾回收分类、标记革除、复制、标记整顿、分代收集、增量收集、分区算法等多种算法

从垃圾回收空间上划分能够分为 Full GC 回收整个堆加上元空间、Minor GC 回收新生代、major GC 回收老年代、mixed GC 回收新生代加老年代

标记革除算法会遍历援用链标记可达对象从而清理不可达对象,会产生内存碎片,速度个别

复制算法不会产生内存碎片,并且速度很快,然而会节约 survivor 区一半空间,并且会挪动对象

标记整顿算法在标记清理根底上减少整顿性能,不会产生内存碎片,但会挪动对象,速度慢

不同的算法有不同的特点,应答新生代能够应用复制算法,应答老年代能够应用标签革除 / 整顿算法

增量收集应用 GC、用户线程交替执行,尽管升高用户响应,但线程切换、吞吐量降落会减少 GC 老本

分区算法将堆内存划分为多个区,依据可能接管的进展工夫来回收性价比高的多个区,进展工夫既在可能接管工夫内,又可能回收性能比高的区

最初(一键三连求求拉~)

本篇文章将被支出 JVM 专栏,感觉不错感兴趣的同学能够珍藏专栏哟~

本篇文章笔记以及案例被支出 gitee-StudyJava、github-StudyJava 感兴趣的同学能够 stat 下继续关注喔 \~

有什么问题能够在评论区交换,如果感觉菜菜写的不错,能够点赞、关注、珍藏反对一下 \~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

本文由博客一文多发平台 OpenWrite 公布!

正文完
 0