多款垃圾收集器一致的设计原则:
收集器应该将Java堆分为不同的区域,然后将回收对象依据其年龄分配到不用的区域之中存储。
1.标记---清除算法
思想:首先标记出所有需要回收的对象,在标记完成后,统一回收掉所有被标记的对象,当然也可以反过来,标记存放的对象。
缺点: 1)产生大量的内存碎片
2)大量的标记和清除操作会使效率低下
2. 标记-----复制算法
思想:将可用内存按照容量大小分为 两等份 。每次只适用其中一块,当这一块的内存用完了,就将存货的对象复制到另一块中。然后再把使用过的内存区域清理掉。
优点: 不需要考虑内存碎片,顺序存储,只有一般内存,而且实现简单,只需要移动堆顶指针就行了。
缺点:不确定性大,如果存活的对象仍然很多,仍然会有效率问题。
内存是宝贵资源,只利用一半太浪费了。
基于以上的思想,人们改进了此方法。引出了 Eden Survivor 的概念。
这个思想用于 新生代 的内存规划。
默认 Eden S1 S2 = 8:1:1
每次分配内存 只使用 Eden 和 一块 Survivor,当发生 垃圾收集 时,将这两个区域仍然存活的对象一次性复制到 另外 一块 Survivor 空间上。然后清理掉那两块区域。
缺点:当对象的存活率很高时,需要进行较多的复制操作,而且当进行到某一次 Minor GC 之后,S 区空间不足了,就需要额外的空间进行分配担保。
3.标记----整理算法
先进行标记,然后让所有存货的对象都向内存空间一端移动,然后直接清理掉边界以外的内存。
其与 标记---清除算法十分类似,只不过多了一步移动。
但是,移动存活后的对象并不是一个十全十美的操作:
在老年代中,大部分都不会被回收,那么大量的移动和引用更新 是一项十分重的工程。
而且有一个致命的问题,必须要暂停用户的应用进程才能进行,俗称 STOP THE WORLD。
在说一下好处:如果不进行对象移动,肯定会产生很多内存碎片,那么当忍无可忍时,我们就只能这么办了,借助OS的非连续内存分配技术,来给我的对象分配内存,那么你的对象内存区域不连续,肯定有一个页表来记录它,这样的话,访存的次数大概率会增加,那么我的应用程序的吞吐量降低了,这是我们不太想看到的。