一、标记-清除算法
算法分为标记和清除两个阶段。
首先标记需要回收的所有对象,然后在标记完成后,统一回收所有被标记的对象。
不足:1.效率问题。标记和清除两个过程的效率都不高
2.空间问题。标记清除之后会产生大量的不连续内存碎片。空间碎片太多,在分配大对象时候没有足够空间,那么就会触发另一次垃圾回收动作。
二、复制算法
复制算法将可用内存按照容量大小划分为大小相等的两块,称为活动区和空白区。当活动区的内存用完了,就将活动区上还存在的对象复制到空白区,然后把活动区的内存空间一次性清理掉。这个时候原先的空白区就变成了活动区,原先的活动区就变成了空白区。
所以复制算法每次针对整个半区进行回收,内存分配时就不用考虑内存碎片的问题。只要移动堆顶指针,按顺序分配内存即可。
但是这种算法的代价是内存小了一半,代价比较大。
三、标记-整理算法
这个算法的过程和标记-清除算法很相似。只是不直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。从而避免了内存碎片的问题。
![]()
![](https://i-blog.csdnimg.cn/blog_migrate/ef5dae36a962688502c0ddb1ba505c75.png)
四、分代回收算法
当前主流的虚拟机都采用分代垃圾回收。它把Java堆分为新生代和老年代,从而对各个年代采用最适当的算法。
在新生代,每次垃圾收集都有大量对象死去,只有少量存活,那就采用复制算法。
新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分成了较大的Eden和两块较小的Survivor空间。每次使用Eden和一块Survivor空间。当回收时,将Eden和Survivor上还活着的对象一次性复制到另外一块Survivor空间,最后清理掉用过的Eden和Survivor空间。他们之间的大小比例是8:1:1。所以只有10%的内存空间会被浪费。当另外一块Survivor空间没有足够的空间存放时候,这些对象直接通过分配担保机制进入老年代。
老年代中的对象存活率高、没有额外的空间对它进行分配担保,必须使用标记-清理或者标记-清除算法进行回收。