您的位置:首页 > 其它

JVM-垃圾回收机制算法简述

2017-07-28 18:05 316 查看
为什么要有垃圾回收机制 

在编程语言的发展中,内存的动态分配与动态回收是必不可少的。当你一直在索取内存空间,而不去释放它,总有一次申请分配时,会出现内存溢出、内存泄漏等问题。这也是为什么我们要去了解这个”自动化”的机制的原因。


1. 标记-清除算法(Mark-Sweep):

其实这很接近标记-清除算法的理念。首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它就是最基础的垃圾回收算法。但是这么做存在两个问题: 

(1)效率问题:标记过程与清除过程采用轮询一样的机制的话,效率太低;

(2)空间问题:需要回收的对象内存空间并不一定是连续的,如果每隔一个单位内存空间就有一个对象需要回收,那么会出现 太多的控件碎片。导致后面的操作在申请较大的内存空间时,会出现实际内存容量足够,但是没有连续的内存空间去分配给这个对象的情况。导致不得不去触发另一次的垃圾收集动作。


2.标记-整理算法(Mark-Compact)

之后学长再次提问:你觉得怎么去解决内存碎片这个问题比较好?“在清理完成之后,把还在的对象一起移动到它们是连续的”。(思维有限,答案只是自己想法) 



其实,标记-整理算法已经把“清除”步骤变成了“整理”。在同样进行标记完成后,让所有存活的对象向内存的一端移动,然后直接清除掉存活对象区边界之外的内存。其实这个算法应用大部分在于分代收集中的老年代中,下面一步一步介绍分代收集。


3. 复制算法(Copying)

为了解决标记-清除算法的效率问题,人们在它的基础上提出了复制算法。其实我更想称呼他为“标记-复制(移动)-清除”算法。



我们可以把内存空间分为大小相等的两份,每次只使用其中的一块。当发现正在使用的一块的内存空间用完了以后,把还存活的对象复制到另一块内存空间上,再去清除上一块内存空间中需要清除的对象。每次操作对半个区域,没有了大量的内存碎片,通过堆指针的移动,效率也得到了提高。 

重要的来了:这种算法存在一个缺陷是把可用内存缩小为了原来的一半。当对象大部分在下一次GC时都会被回收的情况下,50%的比例太大了。所以大部分时候是按照一种实践后的分配方式:
将(新生代)内存空间分为一块80%空间的 Eden 空间和两块10%的 Survivor 空间,在GC时,将Eden与一块Survivor中的存活对象复制到另一块Survivor空间中。

但是实际上,我们无法保证那10%就足以存储还存活的对象,所以我们还要依赖其他的内存空间,就是“老年代”,顺理成章地,上面的空间变成了“新生代”。


分代收集算法

当前的JVM基本都是使用分代收集算法。针对不同的情况,在不同的“代”中执行不同的垃圾回收算法。一般把 JVM堆内存 分为新生代与老年代。新生代对象死亡率高,使用复制算法。老年代中的对象存活率高,没有额外空间对它们进行分配担保,就必须使用Mark-Sweep、Mark-Compact.

对于不同的垃圾收集器,会在不同的代使用大致相同的垃圾回收算法,但是有的是双阶段多线程,有的只有一个阶段使用。稍后,我会分析最近比较热门的CMS收集器与G1收集器中更加智能的过程。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: