JVM(二)垃圾回收

垃圾回收

堆存在垃圾回收机制,

垃圾回收相关面试题

简述Java垃圾回收机制

在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。

GC是什么?为什么要GC

GC 是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存

回收会导致程序或系统的不稳定甚至崩溃,Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动

回收内存的目的,Java 语言没有提供释放已分配内存的显示操作方法。

1.如何判断对象可以回收

1.1引用计数法

引用计数:在对象中添加一个引用计数器,如果被引用计数器加 1,引用失效时计数器减 1,如果计数器为 0 则被标记为垃圾。原理简单,效率高,但是在 Java 中很少使用,因为存在对象间循环引用的问题,导致计数器无法清零。

1.2 可达性分析算法

可达性分析:主流语言的内存管理都使用可达性分析判断对象是否存活。

基本思路是通过一系列称为GC Roots 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程走过的路径称为引用链,如果某个对象到 GC Roots 没有任何引用链相连,则会被标记为垃圾。

可作为 GC Roots的对象包括虚拟机栈和本地方法栈中引用的对象、类静态属性引用的对象、常量引用的对象

1.3四种引用
  • 强引用

    最常见的引用,例如 Object obj = new Object() 就属于强引用。只要对象有强引用指向
    且 GC Roots 可达,在内存回收时即使濒临内存耗尽也不会被回收。

  • 软引用(SoftReference)

    弱于强引用,描述非必需对象。在系统将发生内存溢出前,会把软引用关联的对象加入回收范围以获得更多内存空间。用来缓存服务器中间计算结果及不需要实时保存的用户行为等。仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
    可以配合引用队列来释放软引用自身

  • 弱引用(WeakReference)

    弱于软引用,描述非必需对象。弱引用关联的对象只能生存到下次 YGC 前,当垃圾收集器开始工作时无论当前内存是否足够都会回收只被弱引用关联的对象。由于 YGC 具有不确定性,因此弱引何时被回收也不确定。

  • 虚引用(PhantomReference)
    必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

2.垃圾回收算法

2.1标记清除

在这里插入图片描述

标记阶段:首先从GC ROOTS 出发依次标记有引用关系的的对象,最后清楚没有标记的对象

清除操作:记录该内存空间的开启和结束位置,分配下一个对象进入内存。

优点:速度快

缺点:

  • 执行效率不稳定,如果堆包含大量对象且大部分需要回收,必须进行大量标记清除,导致效率随对象数
    量增长而降低。

  • 存在内存空间碎片化问题,会产生大量不连续的内存碎片,导致以后需要分配大对象时容易触发 FullGC。

2.2标记整理

在这里插入图片描述

过程:从GC root 出发依次标记有引用的关系的对象,让所有存活对象都向内存空间的一端移动,然后边界以外的内存。适用于存货较少。

优点:没有内存碎片

缺点:存活对象移动对系统的开销较大。

标记-清除与标记-整理的差异在于前者是一种非移动式算法而后者是移动式的。如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活的区域,是一种极为负重的操作,而且移动必须全程暂停用户线程。如果不移动对象就会导致空间碎片问题,只能依赖更复杂的内存分配器和访问器解决。

2.3复制

在这里插入图片描述

过程:为了解决内存碎片问题,将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当使用的这块空间用完了,就将存活对象复制到另一块,再把已使用过的内存空间一次清理掉。主要用于进行新生代。

优点:实现简单、运行高效,解决了内存碎片问题。 代价是可用内存缩小为原来的一半,浪费空间。

在对象存活率高时要进行较多复制操作,效率低。如果不想浪费空间,就需要有额外空
间分配担保,应对被使用内存中所有对象都存活的极端情况,所以老年代一般不使用此算法。

3.分代垃圾回收

堆内存大致划分成新生代和老年代

HotSpot 把新生代划分为一块较大的 Eden 和两块较小的 Survivor,每次分配内存只使用 Eden 和其中一块 Survivor。垃圾收集时将 Eden 和 Survivor 中仍然存活的对象一次性复制到另一块 Survivor 上,然后直接清理掉 Eden 和已用过的那块 Survivor。HotSpot 默认Eden 和 Survivor 的大小比例是 8:1,
即每次新生代中可用空间为整个新生代的 90%。新生代主要采用复制垃圾回收算法

老年代
在这里插入图片描述

  • 对象首先分配在伊甸园区域
  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to
  • minor gc 会引发 stop the world,暂停其它用户的线程(只有垃圾回收线程执行,因为会牵扯对象地址的改变),等垃圾回收结束,用户线程才恢复运行
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)(不同垃圾回收器等对象不一样)
  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长,如果还不行,就会爆出outOfMemory

大对象:当新创建的对象大小比新生代的eden和from都要大,会直接进入老年代。如果老年代也没有位置,会爆出outofMemory。

4.垃圾回收器

4.1串行(Serial+Serial Old)

单线程
堆内存较小,适合个人电脑

Serial

最基础的收集器,使用复制算法、单线程工作,只用一个处理器或一条线程完成垃圾收集,进行垃圾收集时必须暂停其他所有工作线程

Serial 是虚拟机在客户端模式的默认新生代收集器,简单高效,对于内存受限的环境它是所有收集器中额外内存消耗最小的,对于处理器核心较少的环境,Serial 由于没有线程交互开销,可获得最高的单线程收集效率。

Serial Old

Serial 的老年代版本,单线程工作,使用标记-整理算法。

Serial Old 是虚拟机在客户端模式的默认老年代收集器,用于服务端有两种用途:① JDK5 及之前与
Parallel Scavenge 搭配。② 作为CMS 失败预案。
在这里插入图片描述

4.2吞吐量优先(parallelGC)

多线程
堆内存较大,多核 cpu ,JDK1.8之后默认的并行的垃圾回收器 在垃圾回收线程执行时,暂停其他用户线程的执行。
让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高

新生代收集器,基于复制算法,是可并行的多线程收集器,与 ParNew 类似。
特点是它的关注点与其他收集器不同,Parallel Scavenge 的目标是达到一个可控制的吞吐量,吞吐量就是处理器用于运行用户代码的时间与处理器消耗总时间的比值。

在这里插入图片描述

4.3响应时间优先(CWS)

多线程
堆内存较大,多核 cpu
尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

以获取最短回收停顿时间为目标,基于标记-清除算法,过程相对复杂,分为四个步骤:初始标记、并发标记、重新标记、并发清除。
初始标记和重新标记需要 STW(Stop The World,系统停顿),初始标记仅是标记 GC Roots 能直接关联的对象,速度很快。并发标记从 GC Roots 的直接关联对象开始遍历整个对象图,耗时较长但不需要停顿用户线程。重新标记则是为了修正并发标记期间因用户程序运作而导致标记产生变动的那部分记录。并发清除清理标记阶段判断的已死亡对象,不需要移动存活对象,该阶段也可与用户线程并发。
缺点:① 对处理器资源敏感,并发阶段虽然不会导致用户线程暂停,但会降低吞吐量。② 无法处理浮动垃圾,有可能出现并发失败而导致 Full GC。③ 基于标记-清除算法,产生空间碎片

工作在老年代的基于标记清除的并发垃圾回收器,在垃圾回收线程的同时可以运行用户线程
在这里插入图片描述

5.垃圾回收调优

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值