前言:什么才算JAVA堆中运行中的垃圾
当一个对象不再被任何对象引用时,这个对象就已经失去了被使用的机会,系统会将其判定为垃圾。
1.引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。这样看来其原理简单,判定效率也很高
但其无法解决相互循环利用的问题:
a.instance()=b;
b.instance()=a;
这样子的情况,两个实例都互相引用,引用计数不为0,但你根本访问不了(说明这两个对象已经没用了),垃圾收集也无法收集。
2.可达性分析算法(追踪式收集)
通过类似树的数据结构来实现,有一个根节点,每次遍历这个树的时候,查看有哪些节点不能到达Root根节点,这些节点就会被垃圾收集。
2.1 GC Root判定
GCRoot算是一个图,虚拟机可以循着图来查询引用关系,只需要和每个引用对象的指针或者标记打交道就行了!
Root不止一个,可以作为根节点的引用可以是
1、线程中的局部变量,形参
2、静态属性引用的类
3、常量引用的类
4、本地方法引用的对象
5、虚拟机内部引用
6、同步锁持有的对象
7、反应java内部情况的回调
3.引用
强引用:只要强引用关系还在,就不会被垃圾收集
软引用:当内存空间不够时,第二次的垃圾收集将会收集软引用关系
弱引用:该引用关系不会生存到下一次垃圾收集了,(只要发生垃圾收集,弱引用直接被清除)
虚引用:不会影响被引用的对象,也不能够获得对象实例,唯一目的是当其被垃圾收集时会发出通知。
4.是否“死亡”的判定
一个对象真正死亡需要两次标记的判定
第一次标记:先查询该对象与GC Root是否有通路,没有则标记一次,然后查看其是否应该执行finalize方法(当该对象没有finalize方法或者该方法已经被虚拟机执行过都将视为没必要执行,没必要执行的,被当作垃圾收集掉,所以每个对象只有一次自救机会),若有必要执行则将其加入F_Queue队列。为了保证队列中每个finalize方法不会进入死循环阻塞队列,虚拟机只确保它执行了finalize方法,不确保其执行完毕。
第二次标记: 执行完finalize方法后,进行第二次标记,还是查与Root有没有通路,所以可以在finalize方法中赶紧找到一个引用自己的方法,与其中一个Root取得连接,拯救自己。
5.方法区回收
主要回收未使用的常量和类
判定类没被使用的三个条件:
1、该类的所有实例全部被回收,包括其派生的子类
2、该类的类加载器被回收,很难实现
3、该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。