标记清除算法
标记清除算法很好理解,主要就是执行两个动作,一个是标记,另一个是对进行标记的对象内存进行清除回收。这个算法有个问题就是会出现内存碎片化严重。如下图所示:
从上图中可以看到,在进行内存回收后出现了严重的内存碎片化,这就导致在分配某些大对象的时候仍然会出现内存不够的情况,但是总体内存确是够的。
复制算法
复制算法的实现方式比较简洁明了,就是霸道的把内存分成两部分,在平时使用的时候只用其中的固定一份,在当需要进行 GC 的时候,把存活的对象复制到另一部分中,然后将已经使用的内存全部清理掉。如下图:
从上图可以看到解决了标记清除的内存碎片化问题,但是很明显复制算法有另一个问题,那就是内存的使用率大大下降,能使用的内存只有原来的一半了。
标记整理算法
既然标记清除和复制算法各有优缺点,那自然的我们就想到是否可以把这两种算法结合起来,于是就出现了标记整理算法。标记阶段是标记清除算法一样,先标记出需要回收的部分,不过清除阶段不是直接清除,而是把存活的对象往内存的一端进行移动,然后清除剩下的部分。如下图:
标记整理的算法虽然可以解决上面两个算法的一些问题,但是还是需要先进行标记,然后进行移动,整个效率还是偏低的。
分代回收算法
JDK分代结构图:Java 堆(Heap)内存简单图_桂花很香,旭很美的博客-CSDN博客_堆内存图
分代回收算法是目前使用较多的一种算法,这个不是一个新的算法,只是将内存进行的划分,不同区域的内存使用不同的算法。根据对象的存活时间将内存的划分为新生代和老年代,其中新生代包含 Eden 区和 S0,S1。在新生代中使用是复制算法,在进行对象内存分配的时候只会使用 Eden 和 S0 区,当发生 GC 的时候,会将存活的对象复制到 S1 区,然后循环往复进行复制。当某个对象在进行了 15 次GC 后依旧存活,那这个对象就会进入老年代。老年代因为每次回收的对象都会比较少,因此使用的是标记整理算法。