问懵逼:谈谈垃圾回收机制与垃圾收集器?

JVM 垃圾回收(GC)模型

  • 垃圾判断算法

  • GC 算法

  • 垃圾收集器的实现和选择

垃圾判断算法

引用计数法(Reference Couting)

算法逻辑:给对象添加一个引用计数器,当一个地方引用它计数器+1,当引用失效计数器-1。任何时刻计算器对象为 0 的对象就是不能再被使用的。算法弊端无法解决循环依赖问题,即 A 依赖于 B,B 也依赖于 A。

根搜索算法(GC Roots Tracing)

HotSpot 使用的也是根搜索算法判定对象是否存活,算法逻辑通过一系列称为 "GC Roots" 的点作为起始向下搜索,当一个对象到 GC Roots 没有任何引用链时则认为此对象不可用。

GC Roots 包括:

  • stack 中的引用;

  • 方法区中的静态引用;

  • Native 方法的引用;

GC 适用的内存区域

方法区

JVM 规范表示这部分区域虚拟机可以不进行 GC 实现,这部分区域的垃圾回收效果比较一般。

目前的商业 JVM 中都有实现方法区的 GC,主要回收两部分内容:废弃常量与无用类。

类回收需要满足的条件:

  • 该类所有的实例都已经被 GC,JVM 中不存在该 Class 的任何实例;

  • 加载该类的 ClassLoader 已经被 GC;

  • 该类对应的 Class 对象没有在任何地方被引用,不能再任何地方通过反射访问该类的方法。

堆内存是 GC 的主要回收区域,在堆内存中,尤其是新生代,常规应用进行一次 GC,一般多可以回收 70~95% 的空间,而方法区的效率远远低于此。

GC 算法

标记-清除(mark-sweep)

算法逻辑分为标记和清除两阶段,首先标记需要回收的对象,然后进行回收。

缺陷:

  • 效率,标记和清除效率不高;

  • 空间,标记清除后会产生大量不连续的内存碎片,导致后续对象分配中无法找到足够的内存而提前触发另一次 GC;

标记-整理(mark-compact)

算法逻辑标记过程和其他算法基本一致,但后续步骤不进行直接清理,而是令所有存活的对象一端移动,然后直接清理掉这段边界以外的内存。

优点不会产生内存碎片,缺陷比标记清除算法会耗费更多的时间进行整理压缩。

执行流程:

1. 标记所有存活对象;

2. 压缩整理存活对象;

复制算法(Copying)

算法逻辑将可用内存划分为两块,每次只用其中一块,当半区的内存用完了将还存活的对象赋值到另一块,然后把原来的整块内存一次性清理掉。

适用场景适合生命周期短的对象,因为每次 GC 都能回收大部分的对象。

优点因为对象只在其中一块内存区域中,当 GC 触发后也是整个内存区域进行回收,不会产生碎片。

缺陷需要两份大小一致的内存区域,对空间利用率不高。

HotSpot 虚拟机默认的 survivor 的 from 和 to 区就是采用该种算法,并且与 eden 的比例是 8:1,即有 1 份用于复制转移用的空间。

执行流程:

1. 从 GC Roots 出发找到对象的引用链;

2. 将存活的对象全部复制到 To 区;

3. 将 from 区整个区域进行垃圾回收;

分代算法(Generational GC)

目前商业虚拟机的垃圾收集都是采用"分代收集"算法,根据对象不同的存活周期将内存进行逻辑划分。

一般会把 Java 堆(Heap)内存划分为新生代(young generation)和老年代(Old generation),这样就可以根据不同代的特点选用最适合的收集算法。

年轻代(Young Generation):

  • 新生成的对象(小对象)会放在年轻代,年轻代使用复制算法进行 GC;

  • 年轻代又分为三个逻辑区域,eden、survivor from、survivor to;

经历多次 GC 后,存活的对象会在 Survivor From 和 Survivor To 之间来回存放,这里有一个前提就是这两个空间有足够的大小来存放这些数据。在GC算法中会计算每个对象年龄的大小,如果达到某个年龄后发现总大小已经大于 Survivor to 空间的 50%,那么这时候就需要调整阈值,将对象尽快晋升到老年代,防止 Survivor 空间不足。

老年代(Old Generation):

  • 老年代存放经历多次 GC 还存活或者大对象;

  • 老年代一般采用标记-清理或者标记-压缩算法进行 GC;

  • 有多种垃圾收集器可以选择,每种垃圾收集器可以看作一个 GC 算法的具体实现;

HotSpot 中的 GC 算法

HotSpot 虚拟机中的垃圾回收类型:

  • 年轻代收集

    • Serial,STW 的单线程复制收集器;

    • ParNew,STW 的多线程复制收集器;

    • Parallel Scanvenge,STW 的复制多线程收集器;

  • 老年代收集

    • Serial Old,STW 的标记-清除-整理单线程收集器;

    • CMS,并发且短暂停顿的收集器;

    • Parallel Old,多线程的压缩收集器;

  • G1 收集器

    • G1 是用于大型堆的垃圾优先收集器,并提供可靠的 GC 短暂停;

    • 在 JDK9 中 G1 收集器被用作默认的收集器;

HotSpot 中提供了多种的垃圾收集器,需要根据具体应用的需求采用不同的收集器。

垃圾收集器的并行(Parallel)和并发(Concurrent)

并行(Parallel):指多个收集器的线程同时工作,但是用户线程处于等待状态。

并发(Concurrent):指收集器在工作的同时,允许用户线程工作。

并发并不代表解决了 STW 的问题,在关键步骤依然需要停顿。比如在收集器标记垃圾时候需要停顿,但是在清除阶段用户线程和 GC 线程可以并发执行。如果在标记过程中用户线程可以工作,那么就会不断有新的对象产生,那么标记的对象就会不准确。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值