垃圾回收算法
分代理论的三个假说
- 弱分代假说:大多数对象都是朝生夕死。
- 强分代假说:熬过越多次垃圾回收的对象越是难以消亡。
- 跨代引用假说:跨带引用相比于同带引用来说是极少数的。
分代收集
将堆划分为新生代与老年代,对于经过多次垃圾回收没有消亡的新生代对象进化成老年代。
对于新生代与老年代使用不同频率去收集。
对于存在跨带引用的暂时不去收集,等新生代进化成老年代之后一块处理。
- 新生代收集:仅仅堆新生代垃圾收集
- 老年代收集:仅仅对老年代收集
- 混合收集:目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收 集器会有这种行为。
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。
标记-清除法
收集分为两个步骤
- 对于已经尚且存活的对象进行标记。
- 将没有标记的全部清楚掉。
反过来标记死掉的,然后清理标记的一样。
缺点
-
效率不稳定
如果对象特别多的话,标记清理起来费死劲。
-
空间碎片
假如ABCCCDDEEEEFG,B、D被清理了,接下来A CCC EEEEFG,现在虽然清理出来了三个,但是我要分配一个需要占据三个位置内存的空间却是没有。
有啥用途
基本不用,据说区块链有跟这个算法差不多的。
标记-复制法
将空间分为两半,分别成为A区与B区,A区存在对象,B区不存在
- 将A区尚且存活的对象复制到B区
- 彻底清空A区内存
下次B区复制到A区,清空B区
好处
- 这下子A区清理起来可快多了
- 这下子也没有空间碎片了
缺点
- 如果A区存活对象多的话,复制还不如标记呢
- 空间利用率只有一半,剩下一半看着
有什么用途
此时不能不提新生代。
新生代对象活不了多久,缺点1似乎能避免。
要是我明确知道,存活率低于50%,是不是也就不用准备一半的空间等待复制了。
IBM公司曾有一项专门研究对新生代“朝生夕灭”的特点做了更量化的诠释——新生代中的对象有98%熬不过第一轮收集。
好家伙,那我2,98分挺好。问题是这是个概率,万一有一次有3%熬过去了呢?那1,9开。
对于新生代来说,再分为三个部分:伊甸园区(Eden),两块生存者区(Survivor)。
然后使用一个生存者区跟伊甸园区存储对象,等清理的时候,将对象复制到闲置的生存者区去。然后清理用过的生存者区与伊甸园区。
最后声明,1,9分是HotSpot虚拟机默认的,不是强制规定。根据需要可以调整。
标记-整理法
第一步还是标记,标记死掉的
第二次类似数组的删除,直接开始覆盖。
整理好了直接就OK了。
一个小问题
移动的时候可老慢了。
而且对象移动操作必须全程暂停用户应用程序才能进行。
一个解决方法
那我效仿操作系统文件存储。我干脆不移动,对象存储也不一定非要连续,我上边再加一层,这一层管理所有。
解决方法中的问题
好复杂,复杂死了。
复杂倒也还行,关键是是不移动了,收集的时候爽了,等分配还有访问的时候就发愁了,还需要额外的计算。最主要是算下来,还不如移动快呢。
矛盾
不移动访问慢,移动的话,需要停止用户应用程序才能进行,响应可能不好。
犹如空间换时间,时间换空间,似乎目前很难调和。
还有一个“和稀泥”的办法,平时用标记-清除法,难以容忍碎片的存在的时候再进行标记-整理法。