什么是垃圾回收机制
程序的运行必然需要申请内存资源,无效的对象资源如果不及时处理就会一直占有内存资源,最终将导致内存溢出,所以对内存资源的管理是非常重要了。
为了让程序员更专注于代码的实现,而不用过多的考虑内存释放的问题,所以,在Java语言中,有了自动的垃圾回收机制,也就是我们熟悉的GC。 有了垃圾回收机制后,程序员只需要关心内存的申请即可,内存的释放由系统自动识别完成。 换句话说,自动的垃圾回收的算法就会变得非常重要了,如果因为算法的不合理,导致内存资源一直没有释放,同样也可能会导致内存溢出的。 当然,除了Java语言,C#、Python等语言也都有自动的垃圾回收机制。
假设有一个对象A,任何一个对象对A的引用,那么对象A的引用计数器+1,当引用失败时,对象A的引用计数器就-1,如果对象A的计数器的值为0,就说明对象A没有引用了,可以被回收。
class TestA { public TestB b ;}class TestB{ public TestA a;}
public class Main { public static void main(String[] args) { TestA a = new TestA(); TestB b = new TestB(); a.b = b; b.a = a; a = null; b = null;// 虽然被设置为null,但是a与b之间依旧存在着循环引用的问题 }}
标记清除算法,是将垃圾回收分为2个阶段,分别是标记和清除。 标记:从根节点开始标记引用的对象。 清除:未被标记引用的对象就是垃圾对象,可以被清理。
初始状态下,所有的目标对象都是为0(未被标记) 待jvm出现有效内存耗尽,就会挂起线程,执行GC线程,进行标记
从根节点进行标记到最后,然后回收未被标记的对象。 清理完毕之后挂起gc线程,重新执行原先被挂起的线程。 而被标记的对象会被重新置0;
标记压缩算法是在标记清除算法的基础之上,做了优化改进的算法。和标记清除算法一样,也是从根节点开始,对对象的引用进行标记,在清理阶段,并不是简单的清理未标记的对象,而是将存活的对象压缩到内存的一端,然后清理边界以外的垃圾,从而解决了碎片化的问题。
复制算法的核心就是:将原有的内存空间一分为二,每次只用其中的一块,在垃圾回收时,将正在使用的对象复制到另一个内存空间中,然后将该内存空间清空,交换两个内存的角色,完成垃圾的回收。 典型的复制算法的落地实现就是:jvm中堆内存的年轻代的gc策略(具体可以看我jvm系列的博客的内存模型的那一部分内容)
前面介绍了多种回收算法,每一种算法都有自己的优点也有缺点,谁都不能替代谁,所以根据垃圾回收对象的特点进行选择,才是明智的选择。 分代算法其实就是这样的,根据回收对象的特点进行选择,在jvm中,年轻代适合使用复制算法,老年代适合使用标记清除或标记压缩算法