一、什么样的对象该回收:没有任何引用的对象。
二、怎么判断对象是否不具有引用:有两种方法,一种是引用计数算法,另外一种是可达性分析算法。
引用计数算法:对象中会有一个引用计数器,每有一个对该对象的引用就会在计数值上+1,当计数值为0的时候,该对象便不再有引用。
可达性分析算法(Java主流算法):会设置一个”GC Roots“对象,该对象可以是各种引用的对象,当一个对象没有到”GC Roots“对象的任何引用链时,那么这个对象就是可回收的;具体如下图所示(该图来自《深入理解Java虚拟机》):
三、为了使gc回收更加灵活,Java为对象分成了如下四种不同的状态:
强引用——普遍的引用类型,只要强引用还在就不会被回收(如:Object obj = new Obje(););
软引用——可有可无的对象,在内存溢出之前会被回收;
弱引用——gc运行时时就会回收的对象;
虚引用——无法通过该引用获取对象的实例,被回收时会收到一个系统通知。
四、垃圾回收算法:
标记-清除算法:顾名思义,将要回收的对象标记然后清除标记对象,其问题就是效率低下,并且会产生大量的内存碎片(内存碎片较多时可能会导致较大的对象申请不到内存空间);该算法是CMS收集器(当时所谓的最快收集器)使用的算法,分为四步:初步标记、并发标记、重新标记、并发清除;它之所以比较快是因为实际上会停止程序运行的只有初步标记与重新标记两个步骤,而比较耗时的并发标记与并发清除则是与程序同时运行的(垃圾回收时为了确保对象状态的确定性会暂时停止所有线程,而重新标记就是为了纠正并发标记时程序运行导致的对象状态的改变);具体原理如下图所示(该图来自《深入理解Java虚拟机》):
复制算法:该算法会将内存分成两个区域,一个区域是空白的,另一个区域才是真正被使用的,当gc的时候,被使用区域会将存活的对象都cp到空白区域中,这样就可以直接将使用区域的对象全部清除从而提高效率(但如果存活率较高,那么效率就会相应变低),并且这样清除会让对象存放比较整齐,只要通过移动指针便可添加新的对象;但其缺点也是比较明显的,会浪费一部分内存作为gc时存放存活对象的不可以用的空白区域;具体原理如下图所示(该图来自《深入理解Java虚拟机》,感觉这张图有点偷懒了):
标记-整理算法:简单的说就是将存活的对象向一端移动,然后以此为界限将其它的内存全部清空;具体原理如下图所示(该图来自《深入理解Java虚拟机》):
分代收集算法:在以上三种算法的基础上诞生的,该算法通过在新生代使用复制算法(由于新生代的对象大都朝生夕死,所以对象存活率较低),在老年代使用标记-清除或标记-整理的算法。
除此之外,通过以上的垃圾回收算法诞生了各种各样的垃圾收集器,前面提到的CMS收集器就是其中一种,不同的虚拟机所使用的垃圾收集器也各不相同;而Java自7开始就使用G1替换掉了CMS,而G1在很多方面与CMS有很多的相同之处,但它比CMS各方面都更加完善,使用的却是”标记-整理“的算法,主要分为以下几个步骤:初始标记、并发标记、最终标记、筛选回收。