jvm 三种垃圾回收算法:标记-清除、复制算法、标记-整理

标记-清除:先标记出GC Roots能关联到的对象,然后清除这些被标记的对象,剩下的就是存活的对象了。

缺点:

1、清除需要被清理的对象后剩下的内存都是破碎的,如果要创建大对象,可能会因为找不到足够的内存而再次触发垃圾收集。

2、标记和清除的效率相对于其他算法来说都不高,标记的原理就是从GC Roots往下遍历,能被遍历到的对象就是存活对象,剩下不能被遍历到的对象就是需要被标记清除的。而清除时,是根据标记,然后一个一个清除。。这个效率就比较低了。

复制算法:大致原理,把内存分为两块,分别命名为A、B,A用于创建对象,B则是空闲内存(暂时不存放任何对象),当要进行垃圾回收时,把A中能被GC roots关联到的对象全部复制到B中,然后把A中的所有内存空闲都清理掉。接下来就把B内存当作A,A当作B,当再次要进行垃圾回收时,重复以上步骤。

复制算法适用于年轻代。

优点:

(1)内存总有一部分是完整的一块,不会出现破碎内存的情况。

(2)一般情况下,存活对象只占所有对象中的很少一部分,百分之九十以上的对象都是要被清理掉的,所以复制存活对象到另一块内存中所操作的对象数量就比较少,这样的话,自然而然效率就高了。像标记-清除算法,是先要标记出所有不能存活的对象才行,意味着要对大多数对象进行操作。

缺点:

1、缺点其实也很明显,复制算法就是使用一部分内存作为备用,把这块内存用于存储存活下来的对象,每次垃圾回收都如此,那就意味着作为备用的那一块内存是被浪费了的。所以,复制算法的缺点就是会浪费一部分内存。

2、不仅需要一部分内存作为备用,还需要其他内存作为担保。当存活的对象占用内存超出备用内存的存储空间怎么办?那么就需要使用担保内存了,把一部分存活率高的对象放到老年代中,以此使得备用内存的空间足够存放剩下的对象。

标记-整理算法:标记出所有能存活的对象,然后这些存活的对象向内存的某一端移动。。把这一端内存从头到尾占满,那整块内存除了这一端之外的空间就都是垃圾对象了。

适用于老年代。

举个例子:数组Object[] array=new Object[10];被当做内存

该数组所有元素都有对象,其中,0、2、3、5、7、9都是能够存活的对象,其他的是垃圾对象。

在标记-整理算法中,移动存活对象从后往前移,用存活的对象填补垃圾对象的空缺,像上面那样,9移动到1,7移动到4,这样的话存活下来的对象在0到5之间了,在这个范围以外的对象都是垃圾对象,直接清理。当然,移动算法不是这么简单的,应该是找大小合适的存活对象去填补空缺,使得内存的一端占用空闲尽量的满。

优点:

1、不会像复制算法一样一定要浪费一部分内存用于存储存活的对象,不需要像复制算法一样需要担保内存。

2、被清理的出来的内存是一整块完整的内存空间,而不是破碎的。标记清除算法清理出来的内存是破碎的,容易再触发GC。

3、在老年代这种对象存活率高的情况下,“整理”这个步骤所花费的消耗也是不高的。

4、但标记整理的内存利用率高。比复制算法高,复制算法有一部分必需浪费。

缺点:当对象存活率低的时候使用复制算法的效率明显更高,因为“标记”这个步骤是需要从GC Roots往关联对象遍历的。所以“标记”的效率也是低的。。。

总结:

复制算法:实现简单,运行效率高,但每次只能使用一半内存,因此内存的利用率不高;

标记整理算法,涉及两个过程,运行效率慢,且整理之后会产生不连续的内存空间

猜你喜欢

转载自blog.csdn.net/qq_36951116/article/details/83301699