GC

标记算法

对象被判断为垃圾的标准

当一个对象没有任何引用指向它时,则判定为垃圾。

引用计数算法

  • 通过判断对象被引用的数量判定对象是否可以回收
  • 每个对象实例持有一个引用计数器,每当有新的引用指向它则计数器加1,当引用失效则计数器减1。
  • 任何引用计数器值为0的对象都可以被标记为垃圾。

优点: 执行效率高,不影响程序正常逻辑执行。
缺点: 无法解决循环引用的问题。

可达性分析算法

通过一系列被称为"GC Root"的对象作为起始点,向下搜索,搜索过程中经过的路径称为"引用链"。
而当某个对象到"GC Root"之间没有任何引用链时,称此对象为不可达。
主流语言的主流实现中,均使用可达性分析算法作为GC过程中标记垃圾的算法。

JAVA中可作为GC Root的几种对象
  • 虚拟机栈中引用的对象 (局部变量表)
  • 本地方法栈中引用的对象
  • 方法区中静态属性引用的对象

回收算法

标记-清除算法(Mark-Sweep)

  • 标记: 通过可达性分析算法标记处内存中不可达对象
  • 清除: 标记完成后,线性遍历内存回收被标记对象,同时清除标记以便下次执行垃圾回收。
    在这里插入图片描述
缺点
  • 效率: 标记和清除两个阶段效率都不高。
  • 空间: 清除后会产生大量大连续的内存碎片,大量内存碎片可能导致当需要分配占用较大内存的对象时,无法找到可用连续内存区域而提前触发下一次垃圾回收。

复制算法(Copying)

通过将内存划分为同样大小的两块区域(对象面和空闲面),每次使用其中一块作为对象内存分配区域(对象面)。
当执行垃圾回收时,将对象面的所有存活对象复制到空闲面,同时直接清除对象面并转化为空闲面。

复制算法适用于对象存活率低的内存区域,当前主流的虚拟机实现都使用复制算法来回收年轻代区域的垃圾。
实现中会将堆内存划分为年轻代和老年代两块(2:1),同时年轻代又划分为一块Eden和两块Survivor(8:1:1)。
当回收时,将Eden和一块Survivor中存活对象复制到另一块Survivor,最后清理掉Eden和Survivor。
当Survivor空间不够用时,需要依赖其他内存(老年代)进行分配担保。这样,当Survivor没有足够内存空间存放上一次年轻代中存活对象时,这些对象将通过分配担保机制直接进入老年代。

在这里插入图片描述

标记-整理算法(Mark-Compact)

  • 标记: 和标记清除算法的标记过程一样
  • 整理: 移动所有存活对象,按内存地址依次排列,最后将末端内存地址之后的空间全部回收。

在这里插入图片描述

  • 解决了标记清除算法产生的内存不连续问题
  • 适用于对象存活率较高的场景

分代收集算法(Generational Collection)

  • 垃圾回收算法的组合拳
  • 按对象生命周期的不同划分出不同的内存区域,分别采用不同的内存回收策略
  • 提高了JVM的垃圾回收效率

JDK7及之前版本堆内存划分
在这里插入图片描述
JDK8之后的堆内存划分
在这里插入图片描述

Minor GC

作用于年轻代,目的是快速回收掉生命周期较短的对象
在这里插入图片描述

对象如何晋升到老年代
  • 在年轻代中经历过一定次数的垃圾回收依然存活的对象 (默认15次)
  • Survivor区中没有足够内存存放MinorGC后依然存活的对象,直接存入老年代
  • 新生成的大对象 (-XX:+PretenuerSizeThreshold)
垃圾回收相关的调优参数
  • -XX:SurvivorRatio Eden和Survivor的比例 默认8:1:1
  • -XX:NewRatio 新生代和老年代和比例 默认1:2
  • -XX:MaxTenuringThreshold 对象从年轻代晋升到老年代需要经过GC的阈值
Major GC(Full GC)

标记-清除
标记-整理
执行Full GC会触发Minor GC,执行效率慢,但频率低。

触发Major GC的条件
  • 老年代空间不足
  • 永久代空间不足(JDK1.7及之前)
  • 年轻代晋升到老年代对象平均大小大于老年代剩余内存空间
  • System.gc()

新生代垃圾回收器

常见的垃圾回收器联系
在这里插入图片描述

Serial收集器(-XX:useSerialGC, 复制算法)

单线程收集,使用时必须暂停其他用户线程。
简单高效,client模式下默认的年轻代收集器
在这里插入图片描述

ParNew收集器(-XX:useParNewGC, 复制算法)

多线程收集,其他特性和Serial收集器一样。
server模式下默认的年轻代收集器
单核模式下效率不如Serial,适用于多核处理器环境。
调优参数 -XX:UseParallelGCThreads 在这里插入图片描述

Parallel Scavenge收集器(-XX:useParallelGC, 复制算法)

比起前面两种关注用户线程停顿时间的收集器,Parallel收集器更关注系统吞吐量。
是server模式下默认的年轻代收集器
调优参数 -XX:UseAdaptiveSizePolicy
在这里插入图片描述

老年代垃圾回收器

Serial Old收集器(-XX:UseSerialOldGC, 标记整理)

单线程,垃圾收集时需要暂停用户线程
简单高效,client模式下默认的老年代收集器
在这里插入图片描述

Parallel Old收集器(-XX:UseParallelOldGC, 标记-整理)

多线程,吞吐量优先。
JDK1.6之后提供,配合年轻代的Parallel Scavenge收集器,适合cpu敏感 计算密集的场景。
在这里插入图片描述

CMS收集器(-XX:UseConMarkSweep, 标记清除)

  • 初始标记: 标记GC Root直接关联的对象。stop-the-world
  • 并发标记: 并发追溯标记,程序不会停顿。
  • 并发预处理: 查找执行并发标记阶段,从年轻代新晋升到老年代的对象。
  • 重新标记: 标记在执行并发标记过程中,由于用户线程继续执行而产生的变化的对象。stop-the-world
  • 并发清理: 清理垃圾对象,程序不会停顿。
  • 在这里插入图片描述

G1收集器(-XX:UseG1GC, 复制+标记-整理算法)

并发和并行:可以利用多核cpu的特性,并行处理垃圾收集过程。且和用户线程并发执行,不会阻塞用户线程
分代收集:年轻代使用复制收集算法,老年代使用标记整理算法
空间整合:基于标记整理收集算法解决内存空间碎片化的问题
可预测的停顿:可建立期待的停顿点。

使用基于G1的垃圾收集器时,会将整个java堆内存划分为多个大小相同的region
年轻代和老年代在物理空间上不再是隔离的。

常见面试题

Q:Object中的finalize()方法和C++中的析构函数是否相同

  • 析构函数的调用时间是确定的,而finalize是不确定的
  • 当触发垃圾回收时,首先根据可达性分析算法确定对象是否可达,如不可达则第一次标记。之后判断是否覆盖finalize方法,如覆盖则将对象放置在F-Queue队列中。最后由虚拟机创建一个低优先级的finalize线程去执行finalize方法。
  • 由于finalize线程的低优先级,方法执行随时可能会被终止。
  • finalize方法给即将被垃圾收集器回收的对象最后一次逃脱清理的机会。

Q:强引用、弱引用、软引用、虚引用

强引用
  • 最普遍的引用,例如Object obj = new Object();
  • 当内存空间不足时,jvm宁可抛出OOM终止程序也不会回收强引用指向的对象
  • 通过将引用置为null来弱化引用或者超出作用域范围,使其指向对象被回收
软引用
  • 用来描述一些有用非必须的对象
  • 当内存空间不足时可以回收软引用指向的对象
  • JDK1.2提供了SoftReference类实现软引用
弱引用
  • 描述有用非必须对象,强度低于软引用
  • 一旦发生GC直接回收
  • JDK1.2提供了WeakReference类实现弱引用
虚引用
  • 不会决定对象生命周期,任何时间都可能被回收
  • 主要起到哨兵的作用,用来跟踪对象被垃圾回收器回收的活动。
  • 只能和引用队列ReferenceQueue一起使用

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值