jvm垃圾回收算法简单记录

回收算法简介

  • 面试题

1.cms是作用于老年代还是新生代,新生代采用的垃圾回收算法是什么,老年代采用的回收算法是什么。这样的好处?

CMS(Concurrent Mark Sweep)收集器是一种以获取 最短回收停顿时间 为目标的收集器。
这是因为CMS收集器工作时,GC工作线程与用户线程可以并发执行,以此来达到降低收集停顿时间的目的。

CMS收集器仅作用于老年代的收集,是基于标记-清除算法的,它的运作过程分为4个步骤:

整个过程分为 4 个步骤,包括:
初始标记:仅仅只是标记一下 GCRoots 能直接关联到的对象,速度很快,需要停顿(STW-Stoptheworld)。
并发标记:从 GCRoot 开始对堆中对象进行可达性分析,找到存活对象,它在整个回收过程中耗时最长,不需要停顿。
重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿(STW)。
这个阶段的停顿时间一般 会比初始标记阶段稍长一些,但远比并发标记的时间短。
并发清除:不需要停顿。
这样,在整个收集周期内,只有两次短暂的暂停(初始标记和重新标记),达到了近似并发的目的。
常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,
GC会收集那些不是GC roots且没有被GC roots引用的对象。

  • CMS收集器特点

CMS收集器优点:并发收集、低停顿(初试标记、重新标记)。

CMS收集器缺点:

CMS收集器对CPU资源非常敏感,需要更多的CPU资源。
CMS的另一个缺点是它需要更大的堆空间。
CMS收集器无法处理浮动垃圾(Floating Garbage)。
CMS收集器是基于标记-清除算法,该算法的缺点都有。

  • 安全点(Safepoint)

安全点,即程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停

新生代采用的垃圾回收算法是复制算法
此GC算法实际上解决了标记-清除算法带来的“内存碎片化”问题。首先还是先标记处待回收内存和不用回收的内存,
下一步将不用回收的内存复制到新的内存区域,这样旧的内存区域就可以全部回收,而新的内存区域则是连续的。
它的缺点就是会损失掉部分系统内存,因为你总要腾出一部分内存用于复制。

在新生代中又分为了三个区域:Eden 空间、To Survivor空间、From Survivor空间,
from survivor 和 to survivor大小相同,且保证一个为empty。

新的对象实例被创建的时候通常在Eden空间,发生在Eden空间上的GC称为Minor GC,
当在新生代发生一次GC后,会将Eden和其中一个Survivor空间的内存复制到另外一个Survivor中,
如果反复几次有对象一直存活,此时内存对象将会被移至老年代。可以看到新生代中Eden占了大部分,
而两个Survivor实际上占了很小一部分。这是因为大部分的对象被创建过后很快就会被GC(这里也许运用了是二八原则)。

Eden:from survivor:to survivor====8:1:1
新生代的垃圾回收称为“Minor GC”。
老年代的垃圾回收称为“Major GC”。

分代收集算法
分代回收是指,堆内存分为新生代和老年代,并采用不同的垃圾回收算法。

其中把新生代分为 Eden区和两个大小相同的 Survivor 区。
新生代的GC 成为 Minor GC。Minor GC 时 Eden 区和 from 指向的 Survivor 区的存活对象会被存储到 to 指向的 Survivor 区。
当 Survivor 区对象复制次数到一定值或者Survivor区空间使用超过一定值的时候,会把对象晋升到老年代。

  • Minor GC如何避免全堆扫描?

针对Minor GC 可能出现的老年代对象包含新生代对象引用的问题,Hotspot虚拟机是用卡表技术来解决的。
Hotspot虚拟机使用了卡表(Card Table)技术去解决这个问题。具体操作是Java虚拟机把整个堆分成一个个512字节的卡,
并且维护一张表用来存储每张卡的标识位。这个标识位代表这张卡是否包含对新生代对象的引用。如果存在就认为这张卡是脏的。

这样在Minor GC的时候就可以不进行全堆扫描,而是从卡表中寻找脏卡,并将脏卡中的对象加入到GC Roots中。脏卡扫描结束后则清空标识位。

GC Roots是指堆外指向堆内的引用,一般有如下几种:
Java方法栈帧中的局部变量
已加载类的静态变量
JNI handles
已启动尚未停止的Java线程

首先我们得搞清楚垃圾的定义是什么,哪些内存是需要回收的。

  • 引用计数算法

引用计数算法(Reachability Counting)是通过在对象头中分配一个空间来保存该对象被引用的次数(Reference Count)。
如果该对象被其它对象引用,则它的引用计数加1,如果删除对该对象的引用,那么它的引用计数就减1,
当该对象的引用计数为0时,那么该对象就会被回收。
引用计数算法无法解决循环依赖的问题。

  • 可达性分析算法

可达性分析算法(Reachability Analysis)的基本思路是,通过一些被称为引用链(GC Roots)的对象作为起点,
从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain),当一个对象到 GC Roots
没有任何引用链相连时(即从 GC Roots 节点到该节点不可达),则证明该对象是不可用的。

通过可达性算法,成功解决了引用计数所无法解决的问题-“循环依赖”,只要你无法与 GC Root 建立直接或间接的连接,
系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于 GC Root。
Java 内存区域
在 Java 语言中,可作为 GC Root 的对象包括以下4种:
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
本地方法栈中 JNI(即一般说的 Native 方法)引用的对象

这里我们讨论几种常见的垃圾收集算法的核心思想。

  • 标记 — 清除算法

标记整理算法(Mark-Compact)标记过程仍然与标记 — 清除算法一样,
但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,再清理掉端边界以外的内存区域。

标记整理算法一方面在标记-清除算法上做了升级,解决了内存碎片的问题,也规避了复制算法只能利用一半内存区域的弊端。
看起来很美好,但从上图可以看到,它对内存变动更频繁,需要整理所有存活对象的引用地址,在效率上比复制算法要差很多。

— 整理算法来进行回收。

  • 分代收集算法

分代收集算法(Generational Collection)严格来说并不是一种思想或理论,而是融合上述3种基础的算法思想,
而产生的针对不同情况所采用不同算法的一套组合拳。对象存活周期的不同将内存划分为几块。
一般是把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记-清理或者标记

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值