对象是否死了
Java堆里面存放着Java世界中几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”(没用引用)了。关于怎么知道对象是否可用在Java世界中有两种!
引用计数
所谓引用计数就是指在new Object()的时候,在对象中存放个计数值,有引用指向就加一,当引用消失是就减一,直到零就可以回收了。但是在现在主流的虚拟机实现中却很少用到引用计数,因为引用计数需要配合好,搞不好还会内存泄漏,比如对象之间的循环引用问题。
举个栗子:两个对象A、B,里面有一个object的属性,A.object=B,B.object等于A,然后这两个对象就再无任何引用,外界也没法通过什么访问到,而且引用计数还不为零,无法进行回收。
package com.yedi.object;
/**
* @author MILELU
* @date 2020/10/26 9:24
*/
public class ReferenceCount {
Object object=null;
public static void main(String[] args) {
ReferenceCount a = new ReferenceCount();
ReferenceCount b = new ReferenceCount();
a.object=b;
b.object=a;
a=null;
b=null;
System.gc();
}
}
GC日志:
[GC (System.gc()) [PSYoungGen: 530K->504K(1024K)] 746K->728K(1536K), 0.0007884 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 504K->420K(1024K)] [ParOldGen: 224K->171K(512K)] 728K->591K(1536K), [Metaspace: 3109K->3109K(1056768K)], 0.0050099 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
可以看到FUll GC里的YongGen从504->420,OdeGen:224k->171K,说明上面的代码还是被回收掉了,可以证明hotSopt不是使用引用计数来判断对象是否存活,因为引用计数这一段代码不会被回收。
可达性分析算法
Java是通过可达性分析算法来判断对象是否存活,而且同时解决了引用计数的缺点循环依赖问题。可达性分析就是通过一个GC Roots根对象作为节点一直连这一条线往下找,有引用的对象就会连着一条引用链,如果某个对象没有引用链相连的话就没法通过GC Roots找到它,就可以知道这个对象是没用的。
在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,比如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,比如Java类的引用类型静态变量。
- 在方法区中常量引用的对象,比如字符串常量池(String Table)里的引用。
- 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized关键字)持有的对象。
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。