标记-清除算法
算法分为“标记”和“清除”两个阶段:
1.标记:垃圾收集器从根节点出发,标记所有被引用的对象,一般是在对象的Header中记录为可达对象。
2.清除:垃圾收集器对堆内存从头到尾进行线性的遍历,如果发现某个对象没有标记为可达对象,则将其回收。
缺点:
1.效率不算高。
2.在进行GC的时候,需要停止整个应用程序,导致用户体验差。
3.这种方式清理出来的空闲内存是不连续的,产生内存碎片,需要维护一个空闲列表。
注意:这里所谓的清除并不是真的置空,而是把需要清除的对象地址保存在空闲的地址列表里面,下次有新对象需要加载时,判断垃圾的位置空间是否足够,如果够,就存放。
复制算法
将可用的内存空间分为两块,每次只使用其中的一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后清除正在使用的内存块中的所有对象,交换两个内存的角色,最后完成垃圾回收。
优点:
1.没有标记和清除过程,实现简单运行高效。
2.复制过去之后保证空间的连续性,不会出现“碎片”问题。
缺点:
1.需要两倍的内存空间。
2.对于G1这种分拆成大量region的GC,复制意味着GC需要维护region之间对象的引用关系,不管是内存占用还是时间开销也不少。
特别的,如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大,或者说非常低,就很适合用复制算法,比如我们的新生代。
标记-压缩(或标记-整理)算法
主要分为两个阶段:
第一阶段和标记清除算法一样,从根节点标记所有被引用对象。
第二阶段将所有的存活对象压缩到内存的一端,按顺序排放,之后,清理边界所有的空间。
优点:
1.消除了标记-清除算法中,内存区域分散的缺点,我们需要给对象分配内存时,JVM只需要有一个内存的起始地址即可。
2.消除了复制算法中,内存减半的高额代价。
缺点:
1.从效率上讲,标记-整理算法要低于复制算法。
2.移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址。
3.移动过程中,需要全程暂停用户应用程序,即STW。
总结:
从效率上说,复制算法是最高的,但是浪费了太多内存。
而为了尽量兼顾上面提到的三个指标,标记-整理算法相对来说平滑一些,但是效率上不尽人意,它比复制算法多了一个标记的阶段,比标记-清除多了一个整理内存的阶段。