浅谈JVM垃圾回收机制

        在准备面试的过程中,发现JVM是一个容易混淆的难点,下面我将从理论依据到具体的垃圾收集器功能简述JVM的垃圾回收机制。

垃圾回收发生在哪里

堆内存空间

垃圾回收的目的是什么

垃圾回收器在不可预知的情况下对于堆内存中已经死亡的或者长时间没有用的对象进行清除和回收,从而有效防止内存泄漏,可以有效的的使用可使用的内存

分代收集理论:

  1. 弱分代假说:绝大多数对象都是朝生夕灭的
  2. 强分代假说:熬过多次垃圾收集过程的对象就越难消亡

依照分代收集理论,市面上的Java虚拟机将堆分为新生代和老年代,新生代中每次垃圾回收存活的少量对象将会逐步晋升到老年代中


垃圾回收流程

  1. 可达性分析判定对象是否存活
  2. 二次标记基本确定被回收的对象集合
  3. 垃圾收集器进行真正的垃圾回收

可达性分析: 通过一系列 GC Roots 作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索锁走过的路径称为引用链,如果某个对象到GC Roots之间没有引用链,用图论理来说叫做不可达。

可以固定作为GC Root的对象:

  • 虚拟机栈中引用的对象例如:局部变量表中的局部变量
  • 方法区中类静态属性对象,例如 引用数据类型那个的静态变量
  • 方法区中常量引用的对象、
  • 本地方法区中 Native方法引用0的对象、
  • 虚拟机内部的运用,异常对象、基本数据类型相应的class对象
  • 所有被synchronized关键字持有的对象、

二次标记: 

  • 对象在可达性分析后发现和GC Root之间没有引用链会被第一次标记
  • 所有的第一次标记对象会进行筛选,条件为是否需要执行finalize()方法
  • 被判定有必要执行finalize()方法的对象放置在F-Queue队列中,后续有一个专门的低优先级线程执行finalize()方法。收集器稍后会对F-Queue中对象进行二次标记。
  • 被执行对象在finalize()方法中只要与引用链上任意对象建立连接就会在第二次标记中被移出即将回收的集合 

 常见的垃圾回收算法

标记-清除算法:首先标记所有需要回收的对象,完成后统一回收被标记的对象

缺点:1. 如果有大量需要回收的对象,标记和清除过程会变长,效率降低

           2. 清除后会产生大量不连续的内存碎片,程序后续运行如果需要分配较大的对象时,会由于没有足够的连续空间再次触发垃圾收集

标记-复制算法:目的:解决清除算法面对大量可回收对象时执行效率低的问题

半区复制:将内存划分为容量相等的两块,每次只是用一块,内存用完,将还活着的对象复制到另一块上,将已经使用的内存空间一次清理掉。在面对多数可回收的情况,只需要复制少数,而且分配内存时不需要考虑空间碎片。实行简单,效率高。

缺点:每次浪费一半的堆内存空间。

升级版:Appel式回收:将新生代的内存布局按照 8:1:1进行分配,大的:Eden 小的:survicor.每次内存分配使用90%(大+1小),发生垃圾收集时,把 两块中存活的对象复制到另一块survivor中,清理使用过的空间。在存活对象超过10%的情况时,会依赖其他内存区域进行分配担保。(一般是将超出部分复制到老年区)

标记-整理算法:老年代的回收算法,针对复制算法对象存活率较高,复制操作较多进行升级。

在标记后,让所有存活的对象向内存空间一端移动,然后直接清理掉边界以外的内存。


 经典垃圾收集器

Serial收集器:诞生于jdk1.3.1之前,是一款单线程新生代收集器,垃圾回收采用标记-复制算法

单线程:在Seria进行垃圾收集时,其他线程必须停止工作(这个时间被称为 停顿时间)

优点:

  • 相比于其他收集器的单线程,它更简单高效
  • 多适用于运行在客户端模式下的虚拟机
  • 在单核或者核心数较小的环境下,由于没有线程的交互开销,可以获得最高的单线程收集效率
  • 在内存资源受限的环境下,额外内存消耗最小

Serial old收集器:老年代单线程垃圾收集器,采用标记-整理算法

ParNew垃圾收集器:新生代垃圾收集器,采用标记-复制算法。本质为Serial的多线程并行版本,在同一时间内有多条GC线程同时进行垃圾收集,不支持并发所以停顿时间依旧存在 。

ParNew更适用于服务端模式下的虚拟机,在JDK7之前是首选的新生代收集器,常用于和老年代收集器CMS搭配使用,JDK9以后ParNew 并入CMS。

优点:在垃圾回收时系统资源利用率高

缺点:在单核心处理器环境下效果不佳,在超线程的伪双核环境下,由于存在线程交互开销,效率不佳

Parallel Scanvenge 收集器:吞吐量有限收集器

吞吐量 = 运行用户代码时间 / 运行用户代码时间 + 垃圾收集时间

垃圾回收时间越短越适合与用户交互,高吞吐量意味着可以高效利用处理器资源

Parallel Scanvenge 收集器,是基于标记复制的新生代可并行多线程收集器,目标是 达到一个可控制的吞吐量。在JDK5 及以前 新生代使用 ps ,老年代使用Serial Old,由于不能并行执行,导致垃圾回收整体效率不高。(Parallel Scanvenge 自带一个 ps marksweep 用于老年嗲,和Serial Old实现机制一样)

两个参数:用于精准控制吞吐量

-XX:MaxGCpauseMillis:控制最大垃圾收集停顿时间,收集器尽力保证回收时间不超过参数设定值。 隐患:参数值设定的过小会导致新生代空间小,垃圾收集次数频繁,导致吞吐量下降

-XX:GCTimeRatio:设置垃圾回收时间占总时间的比率,直接控制吞度量大小

开关参数-XX:+UseAdaptiveSizePolicy:该参数开启后 虚拟机会自动根据当前运行情况收集性能监控信息,动态调整 新生代大小,Eden、Survivor大小比例(标记复制算法:Appel式回收),从而提供最合适的停顿时间或者最大吞吐量。

Parallel Old 收集器:在JDK6提供,用于Parallel Scanvenge 收集器的老年版本,支持垃圾回收多线程并行回收,基于标记整理算法。 在注重吞吐量或者处理器资源比较稀缺的场合,都可以优先考虑p+p组合,是JDK9 之前默认服务端垃圾收集器

CMS收集器 :以获取最短停顿时间为目标的老年代垃圾收集器,大部分时间采用标记-清除算法,特殊情况下采用标记-整理算法,适用于服务端。

四个步骤: 与可达性分析时的两次标记不冲突,可达性分析是在确认垃圾,没有进行回收

  • 初始标记: 需要 暂停其他工作线程,标记GC Root 能直接关联到的对象,速度快
  • 并发标记:从GC Root直接关联对象开始遍历整个对象图,耗时长,但是不会暂停其他工作线程
  • 重新标记:需要暂停其他工作线程,修正在并发标记期间,因为用户程序执行而导致标记产生变动的那一部分对象的标记记录。所需时间:并发标记>重新标记>初始标记
  • 并发清除,清理删除掉标记阶段判断已经死亡的对象,不需要西东存活对象,所以可以与用户线程并发执行。

三个缺点:

  • 在CPU核心数较少的情况下,并发标记与并发收集阶段占用的现成资源会导致程序运行速度变慢。
  • 浮动垃圾问题: 在并发执行的过程中出现的新的垃圾,只能等到下一次垃圾收集时处理。 同时,在并发时,预留出足够空间给用户程序使用,所以对于老年代要设置触发CMS的阈值,JDK5 为68%, JDK6 为92%,但是又带来一个问题:预留空间无法满足程序 分配新对象的需求,此时会触发 冻结当前用户线程,使用Serial Old 进行老年代垃圾收集,这样会导致停顿时间过长,性能降低。
  • 由于采用标记请出算法,在老年代的内存空间中造成了空间碎片,解决方案:加了一个开关参数,规定在进行了参数值指定次数的Full GC后,对碎片进行整理。(Full GC:收集整个堆和方法区)

Garbage First收集器: 可并发垃圾收集器,面向整个堆,整体采用标记-整理算法,局部采用标记-复制算法,适用于服务端。

G1将整个堆划分为多个独立区域 Region,Region是单次回收的最小单元,每个区域根据需要扮演新生代的Eden空间、Survivor空间或者老年代空间,回收前,G1根据回收锁的空间大小和所需时间,在后台维护一个价值优先级列表,最后根据设定的收集停顿时间(后续叫停顿参数)优先处理价值大的Region。这种优先级收集策略也是收集器Garbage First名字的由来。

G1的大致运作过程:

  • 初始标记:标记GC Root可链接的对象,可以在下一次用户线程并发时正确地在Region中分配新对象,需要停顿用户线程,但是借助Minor GC,实际上没有额外时间停顿
  • 并发标记:对堆中对象进行可达性分析,找到需要回收的对象
  • 最终标记:对用户线程进行短暂停顿,处理并发阶段遗留的问题
  • 筛选回收:统计Region数据,根据价值进行排序,根据停顿参数自由选择对个Region构成回收集,把回收集中存活的对象复制到空Region中,在清理掉整个回收集空间。此处涉及存活对象移动,需要暂停用户线程,由多条收集器线程并行处理。

CMS收集器和G1收集器的区别:

  • CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用;
  • G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用;
  • CMS收集器以最小的停顿时间为目标的收集器;
  • G1收集器可预测垃圾回收的停顿时间
  • CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
  • G1收集器使用的是“标记-整理”+“标记-复制”算法,进行了空间整合,降低了内存空间碎片。

G1收集器相较于其他六种经典垃圾收集器是很麻烦的,这里是对G1的回收机制做了简要的说明,后续会写一个G1专栏进行详细讲述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值