为什么需要进行GC?垃圾是指在运行程序中没有任何指针指向的对象
应该关心哪些区域的回收?1:如果不进行垃圾回收,内存迟早会被消耗完
2:垃圾回收也可以清楚内存里的纪录碎片,碎片整理将所占用的堆内存移动到堆的一端,以便分配将需要连续内存空间大的对象
3:随着应用程序所应付的业务越来越大,复杂,用户越来越多,没有GC就不能保证应用程序的正常运行
垃圾回收相关算法:方法区、堆
垃圾回收器可以对年轻代回收,也可以对老年代回收,甚至是全堆和方法区的回收,其中,Java堆是垃圾收集器的工作重点
从次数上讲:
频繁收集Young区
较少收集Old区
基本不懂Perm区(或元空间)
- 标记阶段:
引用计数算法
1:基本思路
对每个对象保存一个整型的引用计数器属性,对于对象A,只要有任何一个对象引用了A,A的引用计数器加一,引用失效时,计数器减一,计数器值为0表示可以对A进行回收
2:优点
实现简单,垃圾对象便于辨识,判定简单,回收没有延迟性(只要计数器为0就可以进行回收,不需要等到内存不够再回收)
3:缺点
需要单独的字段存储计数器,增加了存储空间的开销
每次赋值都需要更新计数器,增加了时间开销
无法处理循环引用问题
可达性分析算法(根搜索算法、追踪性垃圾收集)
1:基本思路:
以根对象集合(GC Roots)为起始点,按照从上到下的方式搜索被根对象集合所连接的目标对象是否可达;内存中存活对象都会被根对象直接或间接连接,搜索所走过的路径称为引用链;如果目标对象没有任何引用链相连,则是不可达的,就意味着改对象已经死亡,可以标记为垃圾对象
2:GC Roots包括哪几类元素?
虚拟机栈中引用的的对象(各个线程被调用的方法中使用到的参数,局部变量等。
本地方法栈内JNI(通常说的本地方法)引用的对象
方法区中类静态属性引用的对象
方法区中常量引用的对象
所有被同步锁synchronized持有的对象
JVM内部的引用:系统类加载器,基本数据类型对应的Class对象,一些常驻的异常对象(如:NullPointerException、OutOfMemoryError)
反映JVM内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等
除了这些固定的GC Roots集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域的不同,还可以有其他对象临时性地加入,共同构成完整GC Roots集合,比如:分代收集和局部回收(如果只针对Java堆中的某一块区域进行垃圾回收,比如只是针对新生代,必须还要考虑到这个区域的对象完全有可能被其他区域的对象所引用,这时候就需要将关联的区域对象也考虑到GC Roots中)
3:注意
如果要使用该算法来判断内存是否可以回收,分析工作必须在一个能保障一致性的快照中进行,这点不满足的话,分析结果的准确性就无法保证
这点也是导致GC进行时必须"Stop The World"的一个重要原因,即时是号称不会发生停顿的CMS收集器中,枚举根节点时也是必须要停顿的
- 对象的finalization机制
Java语言提供了对象终止机制来允许开发人员提供对象被销毁之前的自定义处理逻辑
当垃圾收集器发现没有引用指向一个对象时,即:回收此对象时,总会先调用这个对象的finalize()方法
finalize()方法允许在子类中被重写,用于在对象被回收时进行资源释放,通常这个方法中进行一些资源释放和清理的工作,比如关闭文件,套接字和数据库连接等
注意:永远不要主动调用对象的finalize()方法,应该交给垃圾回收机制调用,理由如下:
1、在finalize()时可能会导致对象复活
2、finalize()方法的执行时间是没有保障的,它完全由GC线程决定,极端情况下,若不发生GC,则finalize()方法将没有执行机会
3、一个糟糕的finalize()会严重影响GC的性能
由于finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态:
可触及的:从根节点开始,可以达到这个对象
可复活的:对象的所有引用都被释放,但是对象有可能在finalize()中复活
不可触及的:对象的finalize()被调用,并且没有复活,就会进入改状态,不可触及的对象不可能被复活,因为finalize()只会被调用一次,只能复活一次
只有在对象不可触及时才可以被回收判定一个对象objA是否可回收,至少要经历两次标记过程:
1:如果对象objA到GC Roots没有引用链,则进行第一次标记
2:进行筛选,判断此对象是否有必要执行finalize()方法:
i:如果对象objA没有重写finalize()方法,或者finalize()方法已经被虚拟机调用锅,则虚拟机视为“没有必要执行”,objA被判定为不可触及的
ii:如果对象objA重写了finalize()方法,且还未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的Finalizer线程触发其finalize()方法执行
iii:finalize()方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记,如果 objA在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,objA会被移出”即将回收“集合,对象会再次出现没有引用的情况,在这个情况下,finalize()不会再次调用,对象会直接变成不可触及状态



