当我们了解了,如何判断垃圾对象之后就需要知道应该使用采用什么方法去清理垃圾对象。
标记清除算法
概述
- 标记清除算法,主要分为两个步骤:第一个步骤就是标记垃圾对象,第二个步骤就是将这些对象从内存中清除。
- 关于第一步标记,需要注意的是,由于Java中查询垃圾对象的算法是可达性分析算法,因此往往标记的都是非垃圾对象,因此对于Java来说如果使用标记清除算法清理垃圾对象,其实清除的是非标记对象。
特点
优势
算法极其简单高效。
劣势
会产生大量不连续的内存碎片,内存的可利用率不高,会频繁触发GC操作。
对象内存分配策略
对于给对象如何在使用标记清除算法后的产生的不连续的内存空间分配空间,有三种分配策略:
首次适应算法(First-fit)
首次适应算法(First-fit)即:在给对象分配内存空间前,先遍历内存空间中空闲的区域,一旦发现有大小适合当前对象大小的空间,就立刻将这块空间分配给这个对象。
- 该算法是性能最好的分配策略,对于内存碎片的产生情况,介于最佳适应算法和最差适应算法之间。
最佳适应算法(Best-fit)
最佳适应算法(Best-fit)即:在给对象分配内存空间前,先遍历内存空间中空闲的区域,一旦发现有大小等于当前对象大小的空间或者遍历完所有空闲内存空间后找到最小的但是可以容纳对象的内存空间分配给该对象。
- 采用这种算法,可以最大程度减少标记清除算法带来的内存碎片问题。但是这种算法往往需要进行所有的空闲内存空间的遍历,造成性能上的问题。
最差适应算法(Worst-fit)
最差适应算法(Worst-fit) 即:在给对象分配内存空间前,先遍历内存空间中空闲的区域,找出最大的内存空间分配给该对象。
- 这种算法由于需要遍历所有的内存空间,性能损耗较大并且给小对象分配大空间会造成严重的内存碎片问题,因此不是很适合作为JVM分配对象内存策略。
图解说明
现有一个11K的对象需要被分配:
- 采用首次适应算法:会被分配到1号区域。
- 采用最佳适应算法:会被分配到2号区域。
- 采用最差适应算法:会被分配到3号区域。
标记复制算法
概述
如果要使用标记复制算法,需要将内存空间分成两个部分用于进行对象复制;
- 首先查询出垃圾对象
- 然后将非垃圾对象复制到另一边的内存空间中去
- 最后对清除原先的内存空间。
特点
优势
- 该算法不会产生内存碎片
- 算法实现相对简单
劣势
- 相对于其他回收算法,该算法需要两倍的内存空间。
标记整理算法
概述
标记整理算法可以算是标记清除算法的优化版,即先进行标记清除在进行内存空间的整理,以此达到避免内存空间碎片的问题。
特点
优势
- 不会产生内存碎片。
- 相对于标记复制算法节省了空间。
劣势
- 在进行内存空间整理时需要消耗额外的性能。
整理算法的分类
整理算法大致上可以分为三大类:
- 随机整理
- 整理之后对象之间的内存相对位置关系可能和整理之前不一致
- 实现相对简单且执行速度快
- 打乱对象之间的内存位置关系可能会违背局部性原理导致缓存行等缓存机制无效
- 线性整理
- 仅仅只是保证有关联关系的对象,按照相对位置关系排列在一起
- 可能会有内存碎片产生
- 滑动整理
- 将对象滑动到内存的一端,即避免了内存碎片的产生又可以保证对象基于内存的相对位置关系
- 目前主流的JVM中的整理算法基本上都是滑动整理的实现。
双指针回收算法
特点
- 属于随机整理算法的一种实现
步骤
主要分为两轮遍历内存空间:第一轮遍历将非空闲内存移动到内存空间的一端,第二轮遍历将GC ROOT的引用修正为整理后的引用;
第一轮的遍历的步骤如下:
- 头指针指向内存空间首地址,尾指针指向内存空间尾地址。
- 头指针向尾部移动至空闲空间,尾指针向头部移动至非空闲空间。
- 当头指针指向空闲区域并且尾指针此时也是指向非空闲区域,将尾指针指向的内容移动到头指针处,两个指针同时移动。
- 直到两个指针相遇或者头指针大于尾指针,此时头指针之前的区域都是非空闲区域,之后(包含头指针指向区域)都是空闲区域。
Lisp2算法
Lisp2算法属于滑动整理算法的一种实现,在标记完所有的存活对象后Lisp2算法执行以下操作:
- 设置 forwarding 指针:每个对象头中都有一个forwarding指针,指向该对象整理后的位置。遍历整个内存空间计算存活对象的 forwarding。
- 更新指针:遍历整个内存空间,根据 forwarding 更新 GC Roots 指针以及所有存活对象的子对象的指针,将指针指向新的位置。
- 移动对象:遍历整个内存空间,根据 forwarding 移动对象并且清除对象中的标记。
分代收集理论
概述
- 当前主流的JVM的垃圾收集器,大多都遵循分代收集的理论进行设计
- 分代收集理论基于三大分代假说:弱分代假说,强分代假说,跨代引用假说
弱分代假说
绝大多数的对象都是朝生夕死的。
强分代假说
存活越久的对象,就越难以成为垃圾对象。
跨代引用假说
存在相互引用关系的两个对象大部分是同生共死的。