logo头像
Snippet 博客主题

Java八股文查漏补缺-JVM-3-垃圾回收

0 写在前面

  Java八股文查漏补缺系列是针对Java八股复习时,针对在面试时候,问的问题可能比较深入,在JavaGuide网站中的一些八股文可能不够用,根据自己的面试经验进行一些学习补充.

  八股文背不如理解,理解深刻,知道底层原理,才能更加牢固。呜呜!直接上题

本文目标:完善JVM垃圾回收!

1. GC只会针对堆吗?

不是的,还有方法区,后面的虚拟机栈GPT可能说的不对,反正GC的区域重点是堆,然后方法区!

image-20240422165037815

2. 如何判定一个对象是否可以回收?

2.1 引用计数法

遇到循环引用,寄

2.2 可达性分析法(*)

通过GC Roots的对象作为起始点回收,不可达的都回收掉

哪些对象可以作为GC Roots对象?

  • 虚拟机栈中、本地方法栈引用的对象
  • 方法区中常量引用、类的静态引用的对象

3. 四大引用类型

  • 强引用:new出来的,不会回收
  • 软引用:内存不够的时候回收
  • 弱引用:被弱引用关联的对象在下次GC时一定回收
  • 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用取得一个对象。为一个对象设置虚引用关联的唯一目的就是能在这个对象被回收时收到一个系统通知。

4. 垃圾回收算法

  • 标记-清除:会产生大量内存碎片

  • 标记-整理:慢!

  • 复制:每次只使用了一半内存,存活的对象复制到另一边上,然后清理当前区域

    新生代中,Eden与from是这样的,每次清理Eden与from,剩下存活的复制到to中

  • 分代:新生代复制,老生代标记-清除或者标记整理

5. 垃圾回收器

5.1 CMS收集器

英文名:Concurrent Mark Sweep 标记-清除,只能对老生代

  • 目标:最短回收停顿时间
  • 流程:

    1. 初始标记:暂停所有线程,记录与GC roots直接关联的对象,需要停顿
    2. 并发标记:启用所有线程,进行GC roots Trace!耗时最长,无需停顿
    3. 重新标记:修正因为上述线程继续运作的一些标记偏差,需要停顿
    4. 并发清除:无需停顿
  • 缺点:

    • 吞吐量烂
    • 无法处理浮动垃圾
    • 有内存碎片

5.2 G1收集器

英文名Garbage-First,对新生代与老生代通吃!

G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。

  • 目标:引入了区域概念,达到了可预测的停顿时间
  • 流程:
    1. 初始标记
    2. 并发标记
    3. 最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录
    4. 筛选回收:对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率
      • 整体上:标记整理
      • 局部上:两个Region是复制算法

6. 内存分配策略

  1. 对象优先在Eden

  2. 大对象直接老生代

  3. 长期存活的放入老生代,有阈值

  4. 动态判定进入老生代,不一定达到阈值:如果在 Survivor 中相同年龄所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象可以直接进入老年代

  5. 空间担保机制:在Minor GC时,JVM检查老生代的最大连续可用空间是否大于新生代所有对象空间,

    • 如果成立,发生MinorGC

    • 否则,查看查看一个设定的值HandlePromotionFailure是否允许担保失败,如果是

      • 查看老年代最大连续可用空间是否大于历次晋升到老年代对象的评价大,如果是

        • 尝试MinorGC
    • 进行Full GC

7. GC触发条件

  • Minor GC:当Eden满,触发
  • Full GC:
    • System.gc()
    • 老年代空间不足
    • 空间担保失败