JVM__java垃圾回收机制
作者:
就是要AC
,
2021-05-16 21:29:24
,
所有人可见
,
阅读 2515
Java垃圾回收机制
参考周志明《深入理解Java虚拟机》
垃圾回收:顾名思义就是释放垃圾占用的空间,防止出现内存泄漏等问题
1、判断哪些对象是垃圾的方式
1.1 引用计数算法
在对象中添加一个引用计数器,没有一个地方引用它,计数器加一,当引用失效时计数器减一
缺陷:需要配合大量的额外的内存才能正确的工作,很难解决相互循环引用的问题
1.2 可达性分析算法
以 GC Roots 为根对象为起始点集,根据引用关系向下搜索,搜索过程走过的路径称为 引用链,如果某个对象到GC Roots之间没有引用链相连,那这个对象就是可回收的垃圾。
通过OopMap这种数据结构来记录引用关系.
另外为了防止频繁更新OopMap,降低存储空间的消耗,就有了只在特定的位置成为安全点进行更新OopMap。
Java语言采用这种机制,在Java中可固定为GC Roots主要下面几种
在虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中类静态属性引用的对象
方法区常量引用的对象(例如字符串)
Native方法引用的对象
所有被同步锁(synchronized关键字)持有的对象
如何保证并发情况下防止把原存活的对象错误标记为垃圾?
三色标记算法 : 按照“是否访问过”分为三种颜色
白色:该对象尚未被垃圾收集器访问过
黑色:该对象已被垃圾收集器访问过,且该对象所有引用全都已经被扫描
灰色:该对象已被垃圾收集器访问过,但该对象的引用没有全部都被扫描
解决方案
增量更新:并发过程中黑色对象一旦新插入白色对象的引用就会变回灰色对象,也就是记录这些灰色对象,然后在重新扫描(CMS垃圾收集器采用这种方式)
原始快照:灰色对象要删除白色对象的引用关系时,会将要删除的引用记录下来,并发扫描结束后,在将记录过灰色对象为根,重新扫描(说白了,就是不会把这个白色对象它当成垃圾?)(G1垃圾收集器采用这种方式)
2、判断哪些类是垃圾
类的回收条件比较苛刻,需要同时满足下面三个条件
1. 该类所有的实例都已经被回收
2. 加载该类的类加载器已经被回收
3. 该类对应的java.lang.Class对象没有在任何对象被引用,即不能通过反射访问该类的方法
3、垃圾收集算法
3.1 分代收集理论
收集器将Java堆划分出不同的区域,然后将回收对象依据其年龄(对象熬过垃圾回收收集过程的次数)分派到不同的区域中存储,一般将Java堆划分成新生代和老年代
新生代,每次垃圾回收都会有大量的对象死去(IBM测量98%熬不过第一轮收集), 存活的对象逐步晋升到老年代中存放,有的虚拟机会将新生代划分成三个区域Eden:Survivor:Survivor = 8 : 1 : 1
3.2 标记-清除算法
标记出所需要的回收对象,在标记完成后,统一回收掉所有被标记的对象,标记的过程就是随想是否属于垃圾的判定过程
缺点:
效率不稳定:堆中包含大量的对象,而且大部分是被回收的,所以会进行大量标记和清除动作,执行效率会随对象数量的增长而降低
内存空间碎片化:标记清除之后会产生大量不连续的内存碎片,太多会导致等到需要分配较大对象无法找到足够连续的内存而提前触发垃圾回收
3.3 标记-复制算法
将内存按容量划分成两块,每次只是用其中的一块,如果这块内存使用完了之后就将还存活的对象复制到另一块区域上去,然后再将已使用过的内存空间一次性清理掉
会导致大量内存空间的开销,但是不用考虑内存空间碎片问题
往往采用这种方式回收新生代,设置两块更为合理的两块区域即 (Eden + Survivor) :Survivor = 9 : 1, 每次分配内存只会使用Eden和一块Survivor,垃圾收集后会将(Eden + Survivor)中任然存活的对象复制到另一个Survivor中,这样只有10%的空间是“浪费”的,之后这两个Survivor轮流当这个被“浪费”的空间,当然这个对应的垃圾收集器会使用额外的机制处理新生代对象存活超过10%的情况
3.4 标记-整理算法
标记之后不是直接对可回收对象进行清理,而是让所有的存活对象都向内存空间的一端移动,然后直接清理边界以外的内存
移动存活对象并更新引用必须暂停用户线程,这种现象被称为Stop The World
4、经典垃圾收集器
主要简单介绍CMS收集器和G1收集器,
4.1、CMS收集器
一种以获取最短的回收停顿时间为目标的收集器, 基于标记清除算法
主要过程:
1. 初始标记:暂停用户线程,仅标记与GC Root能直接关联到的对象,速度快
2. 并发标记:从关联到的对象开始遍历整个对象图的过程
3. 重新标记:因为并发标记的过程中用户线程也在运行,重新标记就是暂停用户线程,标记上一阶段未标记的对象
4. 并发清除:开启用户线程,同时GC线程进行清除垃圾
CMS 优点是并发收集,低停顿,缺点是无法处理浮动垃圾(在标记清除过程中还要新的垃圾生成),基于标记清除算法所以CMS收集器,所以会有大量的空间碎片的产生
4.2、G1收集器
将Java堆分成大小相等的多个独立区域Region,根据回收所得的空间大小以及回收所需时间的经验值,根据记录这两个值来判断哪个区域更具有回收价值,也就是 Garbage First
G1 相关概念:
G1保留新生代和老年代的概念,Eden和Survivor区域的大小动态分配,G1不再坚持固定大小以及固定数量分配区域划分,每一个Region可以根据需要,扮演着新生代Eden、Survivor或老年代空间
Region中还有一类特殊的区域(Humongous),专门用于存储大对象,G1认为只要是大小超过Region一半的对象就是大对象。
CSet:回收集,一组可被回收的分区的集合。在CSet中存活的数据会在GC过程中被移动到另一个可用分区,CSet中的分区可以来自Eden空间、survivor空间、或者老年代。
RSet:记忆集,记录从其他分区指向当前分区的引用,本质上数据结构是一个hash table,目的是解决跨Region之间的引用
G1的优缺点:
用户可以指定期望的停顿时间,不会产生内存空间碎片,不追求一次将整个Java最全部清理干净
但为了垃圾回收会产生更高的内存占用
G1 总结
G1把Java分成多个Region,每个Region中存放着RSet。G1收集的时候扫描区域的GC Roots,然后由GC Roots找到直连的对象,然后找到RSet中引用的对象,以这两类对象进行堆的引用标记。把标记后可回收的Region放到CSet,有时会触发全局标记然后选出部分收集效率高的老年代Region加入到Cset中区,然后清理CSet中回收价值高的Region,完成清理。