JVM-之垃圾回收算法

今天面试菜鸟物流,问到垃圾收集相关问题,因为没做准备(没有想到晚上来电话了,面试官很敬业),脑海的知识都模糊了,答的一踏糊涂,所以还是有必要写下,做个回顾加深下,继续努力,说到JVM的垃圾回收机制,我门首先要明确,垃圾回收主要回收的内存区域是哪里?程序计数器,虚拟机栈,本地方法栈,都是线程私有的,随线程而生随线程而灭,同时栈中栈帧随着方法的进入和退出有条不紊不稳的执行着出栈和入栈操作,每一栈帧分配多少内存是类结构确定下来后就已知的,因此这些区域的内存回收都具备确定性,这几个区域不需要过多的考虑回收的问题,因为方法结束或者线程结束,内存自然就回收了,而Java堆和方法区则不一样,一个接口的多个实现类所需要的内存可能不一样,程序在运行中才会知道创建哪些对象,所以这部分的内存是动态的的分配和动态的回收。

如何判断回收的对象已经"死亡"?

垃圾回收之前要确定哪些对象是存活的,哪些是已死的(既不可能再被任何途径引用的对象),所以需要有方法来判断。
1 . 引用计数算法
引用计数算法是在对象中添加一个引用计数器,一个地方引用它,计数加1,引用失效时减1,当计数为0时,则认为它不可能再被使用,这种放方法实现简单,效率也高,通常情况下是可行的,但是存在 问题 ,什么问题 ?循环引用:即 ObjectA.instance =ObjectB 及ObjectB.instance=ObjectA ,出现这种情况,因为他们互相引用,使得引用计数器都不为0,所以使用引用计数就无法回收它们。
2 .可达性分析算法
主流的程序语言都是通过可达性分析来判定对象是否是存活的。那什么是可达性分析?此算法就是通过一系列被称为GC Roots的对象作为起始点,向下搜索,所走过的路径被称为 引用链,当一个对象到 *GC Roots没有任何引用链相连,也就是GCRootsd到这个对象不可达,则证明该对象是不可用的,它们就会被判定为可回收对象。
GC Roots对象可以包括如下几种:1. 虚拟机栈(栈帧中的本地变量表) 中引用的对象 2. 方法区中类静态属性引用的对象 3.方法区常量引用的对象 4.本地方法栈中JNI引用的对象

以上两种算法可见,判定对象是否可回收,都是与 引用 相关,而引用则又可以分为以下四种:
1 . 强引用:程序中最常见的,如 Object a =new Object();只要强引用存在,就不会被回收。
2 . 软引用:还有用,但是非必须的,软引用关联的对象,在发生内存溢出之前,会把这些对象列进回收范围进行第二次回收,如果第二次回收还没有足够的内存,就会发生内存溢出异常。
3 . 弱引用:被弱引用关联的对象,只能坚持到下一次垃圾回收到来之前,,当回收到来时,无论内存是否足够,都会回收掉弱引用关联的对象。
4 .虚引用:它是最弱的一种引用关系,一个对象是否有虚引用,完全不会对它的生存时间构成影响,唯一目的就是该对象被回收时能够收到一个系统通知。

写了这么多,也该说到回收算法了吧!

当对象通过上述的算法判定为可被回收时,那具体的回收算法过程是如何呢?接下来分别说说

1 .标记清除(Mark-Sweep)
标记清除是最基本的算法,后续的算法都是基于此改进的,分为两个阶段:1.标记2.清除 ,首先将需要回收的对象做上标记,标记完成后做统一的回收,这个算法很基础,主要的不足点存在两个;1.标记和清除的过程效率不高,2.是清除后产生大量不连续的内存碎片,碎片过多回到导致后续有大对象分配时没有足够内村空间,而不得不提前触发一次回收。

2 . 复制算法
复制算法时将内存分为大小相等的两块,每次使用其中的一块,这一块用完了,就将还存活的对象复制到另一半内存上去,然后将使用过的那一半整体回收,这样就是对整个半区进行回收,内存分配时也就不用考虑碎片问题,分配时移动指针按序分配内存即可,这种算法简单,高效,但是存在一点 不足,那就是每次只能使用一半内存,占比有点高,其实在现代的商业虚拟机中,这种算法被用来回收新生代,研究表明,新生代中的对象98%是“朝生夕死”,所以无需按1:1划分,将内存划分为一块较大的Ed en空间和两块较小的Survivor空间,每次使用Eden和一块Survivor当回收时,将Eden和Survivor中存活的对象复制到另一块Survivor,最后清理掉已使用的Eden和Survivor,HotSpot虚拟机默认Ed en和Survivor的大小比例时8:1,所以新生代可用内存为整个新生代的90%,但是我们无法保证每次回收后存活的对象内存占比就小于10%,那另一个Survivor内存就不够用了,这时候,就要依靠老年代进行分配担保,就是另一块Survivor不够存放回收剩余的对象时,这些对象直接通道分配担保机制进入老年代。

3 . 标记整理
当对象存活率不高时,复制算法效率较高,反之则效率会变低,这种算法不适用于老年代,所以根据老年代特点,提出了标记-整理算法,标记过程和标记-清除一样,但是后续不是对标记的对象直接进行回收,而是让存活的对象向一边移动,然后直接清除掉端边界以外的内存。

4.分代收集算法
目前商业虚拟机,都是次啊用分代收集,其实分代收集算法,就是根据对象存活的周期不同将内存划分为新生代,老年代,然后分局各个年代的特点,采用以上不同的算法,如,新生代每次收集都有大量对象死去,少量存活,那就适合复制算法,只 需要少量复制成本,即可完成收集。老年代,对象存活率高,没有额外空间对其进行分配担保,所以必须使用标记-清除或者标记整理

以上就是对几种回收算法的思想介绍,具体的算法是体现在不同的垃圾收集器之上,后续再写具体对应的收集器。

推荐参考书籍:深入理解Java虚拟机

你可能感兴趣的:(JVM-之垃圾回收算法)