2023.9.2 关于 JVM 垃圾回收机制(GC)

目录

为什么要有垃圾回收机制?

STW(Stop The World)问题

垃圾回收机制主要回收哪个内存区域?

垃圾对象判断算法

引用计数算法

可达性分析算法

垃圾对象回收算法

标记清除算法

复制算法

标记整理算法

分代算法


为什么要有垃圾回收机制?

  • 自动内存管理:垃圾回收机制使得内存的分配释放过程自动化,开发者不需要手动跟踪和释放不再使用的内存,减轻了开发工作量
  • 避免内存泄漏:内存泄漏指程序中分配的内存空间无法被回收和重用,导致内存资源的浪费和耗尽,而垃圾回收机制可以自动检测和回收这些不再使用的内存,从而避免内存泄漏
  • 解决内存碎片化:内存碎片化指内存空间的可用部分被分割成多个小块,无法满足大块内存的分配请求,垃圾回收机制可以通过整理和压缩内存空间,将分散的小块内存合并成更大的可用内存块,从而减少了内存碎片化问题
  • 提高程序性能:垃圾回收机制虽然会引入额外的性能开销,但相比手动内存管理,它通过智能算法来判断哪些对象是可回收的,从而避免了不必要的内存分配和释放操作,可以更高效的管理内存资源,从而提高程序的整体性能
  • 内存安全:垃圾回收机制可以检测和处理悬空引用和野指针等内存安全问题,它可以识别不再被引用的对象,并再必要时进行回收,避免了程序访问无效内存的风险

STW(Stop The World)问题

垃圾回收机制最大的问题就是会引入额外的 空间+时间 开销

  • 空间上:额外消耗 CPU 和内存资源
  • 时间上:STW 问题

STW 问题 指程序运行到需要用 GC 释放内存的时候,可能会导致程序的响应变慢,反应到用户便可能存在明显的卡顿


总结:

总体来说,JVM 垃圾回收机制是 Java 语言的重要特性,它为开发者提供了方便、安全、高效的内存管理方式,减少了内存相关的错误和问题,其开发效率是大于运行效率的!

垃圾回收机制主要回收哪个内存区域?

  • GC 主要是针对 进行释放回收
  • GC 以 对象为基本单位 进行回收

垃圾对象判断算法

引用计数算法

  • 引用计数算法被 Python、PHP 所采用,非 Java 所采用的算法

具体思路:

  • 每个对象都有一个关联的计数器,用于记录当前对象被其他对象引用的次数
  • 每多一个引用指向该对象,计数器就 +1
  • 每少一个引用指向该对象,计数器就 -1
  • 当对象的计数器为 0 时,将被视为垃圾对象,可被垃圾回收器回收 

缺点:

  • 内存空间利用率低:对于小对象来说,可能一个计数器的所占内存比其本身还大
  • 存在循环引用的问题


可达性分析算法

  • 可达性分析算法为 Java 所采用的垃圾对象​​​​判断算法

具体思路:

  • 根集确定(GC Roots):垃圾回收器首先要确定根集的内容,其根集可为 栈中引用的对象方法区中类静态属性引用对象方法区中常量引用的对象本地方法栈中 Native 方法引用的对象

  • 根集遍历:从根集中的每个对象开始,通过对象之间的引用关系,递归地遍历对象图。遍历过程中,将访问到的对象标记为"可达"

  • 标记阶段:标记阶段是遍历过程中的核心步骤,通过对每个对象进行标记,将其标记为可达或不可达。已经标记为可达的对象说明它们仍然被根集或其他可达对象引用,而未标记的对象则被判定为垃圾对象

  • 清除阶段:在标记阶段完成后,垃圾回收器会遍历整个堆内存,将未标记的对象进行清除,释放其占用的内存空间。清除后的内存空间可以被重新利用

优点:

  • 解决循环引用:通过从根集出发,仅标记可达的对象,不可达的对象将被判断为垃圾,即使存在循环引用也能正确回收
  • 高效性:可达性分析算法的效率较高,它只对可达对象进行标记,不需要扫描整个堆内存

垃圾对象回收算法

标记清除算法

基本步骤:

  • 标记阶段:通过可达性分析算法,将可达的对象进行标记,未标记的对象则被标记为垃圾
  • 清除阶段:垃圾回收器遍历整整个堆内存,将未标记的对象进行清除,清除后的内存空间可以被重新利用

缺点:

  • 内存碎片化:标记清除算法会在清除阶段产生内存碎片,即被标记未垃圾袋对象所占用的内存空间,从而导致被释放的空闲空间是零散的不是连续的,可能会导致申请大一点连续内存空间的时候申请失败!
  • 垃圾回收的停顿时间:在标记和清除过程中,垃圾回收器需要暂停应用程序的执行。这种停顿时间可能导致应用程序的响应性能下降,特别是当堆内存较大且垃圾对象较多时


复制算法

  • 针对内存碎片问题,引入的算法

基本步骤:

  • 标记阶段:通过可达性分析算法,将所有可达对象进行标记
  • 复制阶段:在复制阶段,垃圾回收器将标记对象从 From区 复制到 To区 复制过程中,对象的存储地址发送变化,即对象被移动到 To 区
  • 更新引用阶段:完成复制阶段后,需要更新所有指向被复制对象的引用,使其指向对象在 To区的新地址
  • 交换空间:完成引用更新后,From区 和 To区 的角色互换,这样下一次的垃圾回收操作将在新的 To区 进行

缺点:

  • 内存开销:复制算法需要将存活对象复制到 To区,因此需要额外的内存空间来存放复制的对象,从而会增加内存的开销,尤其当存活对象较多时
  • 复制操作的时间开销:复制算法需要将存活对象复制到 To区,这涉及对象的拷贝操作,可能会增加垃圾回收的时间开销。

标记整理算法

  • 针对复制算法内存开销大,引入的算法

基本步骤:

  • 标记阶段:通过可达性分析算法,将所有可达对象进行标记

  • 整理拷贝阶段:垃圾回收器从堆的起始位置开始,依次复制所有活动的对象到堆的另一端,并按顺序排列。拷贝过程中,对象的存储地址发生变化

  • 整理更新引用关系阶段:在拷贝阶段中,垃圾回收器会更新所有指向被复制对象的引用,使其指向对象在新位置的地址。​​​​​

  • 更新根集:在整理阶段完成后,垃圾回收器需要更新根集中的引用,使其指向对象在新位置的地址

  • 空间回收:整理阶段完成后,垃圾回收器可以释放整理前的内存空间,从而减少内存碎片

缺点:

  • 整理过程的时间开销:标记整理算法需要将存活对象整理并移动,这可能涉及对象的拷贝和引用更新操作,增加了垃圾回收的时间开销

  • 停顿时间:在整理阶段,垃圾回收器需要暂停应用程序的执行。这种停顿时间可能影响应用程序的响应性能,特别是当堆内存较大且存活对象较多时


分代算法

  • 总和上述三种算法,通过区域划分,实现不同区域使用不同的垃圾回收算法,从而提高垃圾回收的总体效率

经验规律:

  • 大部分对象在其生命周期中熬不过一轮 GC,即很快成为垃圾,而被回收
  • 一个对象能经过多轮 GC 而不被回收,说明该对象大概率还会长时间的继续存在下去

算法解释: 

新生代:

新生代用于存放新创建的对象。通常采用 复制算法 作为垃圾回收策略。新生代被划分为两个区域:伊甸区和两个生存区。新创建的对象首先分配在伊甸区,当伊甸区满时,仍然存活的对象将被复制到一个生存区,然后在两个生存区之间进行复制和清理。经过多次复制后仍然存活的对象会被晋升到老年代

老年代:

老年代用于存放存活时间较长的对象。由于老年代的对象存活时间更长,GC 的频率相对较低,因此采用 标记整理算法。老年代的垃圾回收相对较少频繁,因为大多数对象在新生代就被回收了

优点:

  • 针对对象生命周期的优化:分代算法通过针对不同代的不同垃圾回收策略,针对对象的生命周期进行优化。新生代采用复制算法,可以迅速回收大部分短命对象,而老年代采用更成熟的垃圾回收算法,适应长寿命对象的特点。

  • 减少垃圾回收的范围:由于大部分对象很快就变得不可达,将堆内存划分为代可以将垃圾回收的范围缩小到新生代,从而减少了垃圾回收的开销。

  • 提高垃圾回收的效率:通过根据对象生命周期的特点采用不同的垃圾回收策略,分代算法可以提高垃圾回收的效率。新生代采用复制算法,具有高效的回收速度,而老年代采用更成熟的算法,可以更好地处理长寿命对象。

注意:若新创建的对象非常大,则直接进入老年代,因为一个大对象进行复制算法,其开销比较大,而且因为它是一个大对象,费很大的开销所创建出来的,很大可能是不会立即销毁的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

茂大师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值