以前看过了垃圾回收算法的新生代GC,也是使用的一种比较浪费内存的复制算法,晚上看书又接着往下看了一点,面试
堆 = 新生代+老年代,可是要注意一点老年代不包括永久代(方法区),也就是说堆内存中只有新生代和老年代,而永久代是指的方法区。算法
以前介绍过新生代中的垃圾回收机制了,再来介绍一下老年代的垃圾回收机制里面使用到的算法。数组
新生代GC:MinorGC 以前介绍过了不说了,复制算法图解也比较清晰线程
老年代GC:FullGC 咱们先说FullGC出现的缘由吧,FullGC是老年代的GC,在新生代若是说存在的对象或者说新建立 出来的对象因为某些缘由须要移动到老年代中,可是老年代中压根就没有这么大的内存空间去容纳这个对象, 那么就会引起一次FullGC,若是在执行完FullGC以后,仍是没有办法给这些对象分配内存,那么凉了,该抛出异常了,异常类型就是OutOfMemoryError。3d
而FullGC使用的是和MinorGC不同的算法,它使用的是标记清除算法,听名字,挺好理解的,来波图示解析一波。 深刻了解JVM一书中的图示是这个样子的,xml
看名字的话是先标记,而后在删除。这也是也给最最基本的算法。 这个算法就是分两个步骤对象
•标记(Mark)过程:找到全部的能够访问的对象,作个指定的标记。blog
•清除(Swep)过程:遍历堆内存,把未标记的对象进行一个回收。递归
以前看一些文档上说,先标记,而后把没有标记的对象给回收掉,其实意思都差很少,可是在深刻理解JVM一书中说到,首先标记出全部的须要回收的对象,在标记完成以后统一回收全部的被标记的对象, 其实个人理解和书中感受有点不太同样,不过区别也不大。我说说个人理解吧。内存
在了解了这个以后,咱们还得说一个概念,那就是GC Root,Root咱们能够理解成一个根节点就像这个样子
上图中的a,b,c,d,就是活着的对象,若是说存在这引用,好比说b引用的a,那么a他就是属于活着的对象。 当咱们老年代内存区中的有效的内存空间不够的时候,那么这时候整个世界都要安静下来了(stop the world),这时候就要开始准备进行垃圾回收了。
•标记:遍历全部的GC Roots,而后将全部GC Roots可达的对象标记为存活的对象。就是咱们图中所标记的a,b,c,d.•清除:清除的过程将遍历堆中全部的对象,将没有标记的对象所有清除掉。 也就是说,若是内存不够,GC线程就会被触发而后将程序暂停,随后将依旧存活的对象标记一遍,最后再将堆中全部没被标记的对象所有清除掉,接下来便让程序继续恢复运行。
流程图就想这个样子的 初始下的老年代中的对象状态
这时候都是没有被标记的状态,接下来内存不够,GC线程中止,开始进行标记了
按照根节点开始遍历 标记的abcdeh都是存活的对象,接下来开始标记。
接下来就是清除数据了,这个就更加的简单了
清楚完成以后还有就是把标记去除掉,能够下次进行标记清除的时候继续清除
这样标记清除就执行完毕了,剩下还有两个要说的地方,
一是在进行标记清楚算法的时候为何要让程序中止,(stop the world)。
二是标记清除算法的优势和缺点又是什么?(Stop the World)
程序中止其实能够理解,由于若是说不中止程序的话,咱们在标记完成这个b对象以后,咱们new出一个新的对象J,能够从B指向J,也就是说,这时候J应该是被标记的状态,可是实际状况确定不是,这个对象在B标记完以后,立刻都要结束了,咱们又new出来一个对象,可想而知,他确定是没有被标记的,因此在第二阶段进行清除的时候,这个苦命的J将会被清除掉, 那这样确定是不符合咱们的实际状况的。
你想呀这刚刚new出来的对象结果被清除了,突然变成了空值,那就不符合咱们的要求了。因此他会让程序先中止,而后不会再出现这种状况,而后进行开始标记阶段。
首先咱们能够先看缺点,他的缺点很是明显,
•由于他会递归遍历Root,这样的话 Stop the World的时间就比较长了,这样一直让人等待的滋味可不是那么好受。•第二个就是这种清除方式清除出来的内存空间是不连续的,你看这个图
死亡的上下分红了2部分,是不连续的,这样给JVM又形成了一种额外的负担,他须要去维持一个内存的空闲列表,若是说咱们在这时候去new一个数组,你想一想他去找这个连续的内存空间的话,是否是就要困难不少呢?
他的优势也有,
•好比说不会出现循环引用, 咱们能够想一想 两个类 互相引用,A中newB,B中newA,那这样岂不是a.b=b ,b.a = a ,是吧 ,而标记清除算法在走完了以后,是能够回收a,和b的,由于他是从根元素开始遍历标记,也就是从ab开始,那么单一的a和单一的b就是没有被标记的,因此,这样就避免了循环引用的问题•还有一点感受没啥区别,都是内存不够的时候才进行的引用。这没啥说的。
而由于标记--清除算法会致使内存分配都出现了各类不均匀的空间,这时候就有了另外的一种算法,直接把那些存活的对象标记出来,而后给他怼到内存空间边界,而后剩下的直接全给他清除了。这方法图解看的一清二楚,剩下的都是和标记清除算法同样的,好像没啥解释的,直接上图
书中你看就是把存活的都给怼到内存空间的上边,你也能够随便的理解成上下左右都ok。
以上就是堆内存中的老年代的两种垃圾回收算法了,若是有不合适的,但愿大佬能够指正,一块儿讨论一下。
Java 极客技术公众号,是由一群热爱 Java 开发的技术人组建成立,专一分享原创、高质量的 Java 文章。若是您以为咱们的文章还不错,请帮忙赞扬、在看、转发支持,鼓励咱们分享出更好的文章。
关注公众号,你们能够在公众号后台回复“博客园”,免费得到做者 Java 知识体系/面试必看资料。