Java GC 快速回顾

Java GC 快速回顾

如何判断对象是否死亡?

2种方法:

  • 引用计数
  • 可达性分析

标记对象真正死亡的过程?

3个步骤

  1. 通过可达性分析,标记并筛选对象(是否有必要执行finalize()
  2. 将筛选过后的对象加入F-Queue
  3. JVM开启finalizer线程处理F-Queue

GC Roots大概有哪些?

  • 局部变量、静态变量、常量的引用
  • Class类型对象
  • JNI对象(Native方法引用的对象)
  • synchronized关键字持有的对象

方法区怎么回收?

方法区要回收的主要是:不用的class对象废弃的常量

class对象如何回收?

满足以下的3个条件可以回收

  1. 没有该类型的实例
  2. 该类型的类加载器已经卸载
  3. 没有对class的引用

常量如何回收?

与Java堆对象回收相似

为什么没有静态变量?

静态变量在class对象里

四种引用类型?

  • 强引用
  • 软引用:没内存了就收集
  • 弱引用:活不过一轮
  • 虚引用:只能用来通知一个对象被回收了,无其他用处

分代收集理论?

  • 新生代朝生夕灭
  • 老年代不容易灭
  • 跨代引用占极少数

如何处理跨代引用?

2

  1. 在新生代建立全局数据结构,记忆集(Rememebered Set),记录哪些老年代有跨代引用
  2. Minor GC扫描时,将这些内存中的对象加入GC Roots

垃圾回收算法?

3种算法

标记清除 :生碎片

标记整理 :耗时间

标记复制:耗内存

垃圾回收之前具体要做的事?

具体如何枚举GC Roots

根节点枚举必须暂停用户线程。不是去挨个内存的找,通过 OopMap 记录有对象引用的地方,直接查找OopMap

OopMap是什么?

类加载时即时编译时在特定的位置,记录下对象的位置

什么时候暂停运行的用户线程?

在线程到达安全点时,也就是OopMap记录的位置就是安全点的位置

如何暂停运行的用户线程?

2种方法:

  • 抢先式中断:先全部中断,然后一个个看谁没到安全点,谁就重新开始跑,直到安全点
  • 主动式中断:设置一个flag,执行中一直轮询flag,如果flag && safe point那么可以暂停了

什么时候暂停Sleep或者Blocked的用户线程?

安全区域

具体如何处理跨代引用?

抽象上是通过一个记忆集Remembered Set来记录跨代引用,具体实现上使用卡表

具体卡表?

卡表记录的是老年代哪些内存区域存在跨代引用指针,而不用知道具体哪个地方有

其中内存位置记录有3种精度:

  • 字长精度:这个内存地址就是跨代引用
  • 对象精度:这个对象里面有跨代引用
  • 卡精度:这一个内存区域里面有跨代引用

Hotspot默认的卡表标记逻辑,:

CARD_TABLE [this address >> 9] = 0

具体如何维护卡表?

通过写后屏障,即在进行对象赋值时,(AOP)在虚拟机层面在该语句后面加上post_write_barrier()

卡表共享写如何解决?

在写入之前,进行判断。判断这个区域是否已经有跨代引用了,有就不写了

具体如何进行并发可达性分析?

GC线程用户线程并发,要保证在一次标记过程中保持一致性,有2种方法

  • 增量更新
  • 原始快照

垃圾收集器

CMS (Concurrent Mark Swap)

面向对象

低停顿,适用于B/S架构

回收过程

  1. 初始标记
  2. 并发标记
  3. 重新标记
  4. 并发清除
初始标记

Stop World 标记通过GC Roots直接关联的对象

并发标记

遍历GC Roots的图

重新标记

Stop World 通过增量更新标记在并发标记过程中用户线程产生的垃圾

并发清除

标记-清楚算法

缺点

  1. 标记清除算法带来的内存碎片
  2. 并发清除过程中带来的浮动垃圾提前了CMS启动的时机,要预留内存给用户线程以免出现失败进行FULL GC
  3. 多并发,资源敏感

G1

面向对象

低停顿,吞吐量

回收过程

  1. 初始标记
  2. 并发标记
  3. 最终标记
  4. 筛选回收
初始标记

Stop World 标记通过GC Roots直接关联的对象,并且修改TAMS指针(Top at Mark Start)

并发标记

遍历GC Roots的图

最终标记

Stop World 通过原始快照去修正并发标记中产生的垃圾

筛选回收

整体标记-整理局部标记-复制通过对每个Region估值排序然后回收,更新Region的统计信息

缺点

  1. 为每个Region维护记忆集并且是双向的(CMS是单向的),内存占用大

  2. 因为记忆集维护复杂,所以写屏障也复杂,异步计算(CMS同步计算)计算量增大

猜你喜欢

转载自blog.csdn.net/qq_43709922/article/details/113546941