1、Java引用的种类
1、1 对象在内存中的状态
class Node {
Node next;
String name;
public Node(String name) {
this.name = name;
}
}
public class NodeTest {
public static void main(String[] args) {
Node n1 = new Node("一号节点");
Node n2 = new Node("二号节点");
Node n3 = new Node("三号节点");
n1.next = n2;
n3 = n2;
n2 = null;
}
}
上面程序中定义了三个Node对象,并通过合适的引用关系把这三个Node对象组织在一起,下图为JVM中对应的有向图。- 可达状态:当一个对象被创建后,有一个以上的引用变量引用它。在有向图可以从起始顶点导航到该对象,那么它就处于可达状态,程序可以通过引用变量来调用该对象的属性和方法。
- 可恢复状态:如果程序中某个对象不再有任何引用变量引用它,它将先进入可恢复状态,此时从有向图的起始顶点不能导航到该对象。在这种状态下,系统的垃圾回收机制准备回收该对象所占用的内存。如果系统调用finalize方法重新让一个以上的引用变量引用该对象,则这个对象会再次变为可达状态;否则,该对象将进入不可达状态。
- 不可达状态:当对象的所有关联都被切断,且系统调用所有对象的finalize方法依然没有使该对象变成可达状态后,这个对象将永久性地失去引用,最后变成不可达状态。只有当一个对象处于不可达状态时,系统才会真正回收该对象所占有的资源。
public class StatusTranfer {
public static void test() {
String a = new String("Java对象1"); //①
a = new String("Java对象2"); //②
}
public static void main(String[] args) {
test();
}
}
当程序执行test方法的①行代码时,代码定义了a变量,并让该变量指向“Java对象1”字符串。该代码执行结束后,“Java对象1”字符串对象处于可达状态。1、2 强引用
这是Java程序中最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量,这个引用变量就是强变量。当一个对象被一个或一个以上的强引用变量所引用时,它处于可达状态,它不可能被系统垃圾回收机制回收。强引用时Java编程中广泛使用的引用类型,被强引用所引用的Java对象绝不会被垃圾回收机制回收,即使内存非常紧张;即使有些Java对象以后永远也不会被用到,JVM也不会回收强引用所引用的Java对象。1、3 软引用
1、4 弱引用
弱引用和软引用有点类似,区别在于弱引用所引用对象的生存期更短。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。但是并不是当一个对象只有弱引用时,它就会立即被回收,而是等到系统垃圾回收机制运行时才会被回收。import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) {
String str = new String("Java对象");
WeakReference<String> wr = new WeakReference<String>(str);
str = null; //切断str引用和"Java对象"字符之间的引用
System.out.println(wr.get()); //①
System.gc(); //强制垃圾回收
System.runFinalization();
System.out.println(wr.get()); //②
}
}
输出结果为:null
当执行str = null;之后,切断了str和“Java对象”字符串对象之间的引用关系,此时只有一个弱引用对象引用字符串对象,程序中①行代码依然可以输出“Java对象”。如果系统垃圾回收机制启动,只有弱引用的对象就会被清理掉。程序中②行代码输出null,这就表明对象已经被清理了。
1、5 虚引用
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
public class PhantomReferenceTest {
public static void main(String[] args) throws Exception {
String str = new String("Java对象");
ReferenceQueue<String> rq = new ReferenceQueue<>(); //创建引用队列
PhantomReference<String> pr = new PhantomReference<String>(str, rq); //创建虚引用
str = null;
System.out.println(pr.get()); //①
System.gc(); //强制垃圾回收
System.runFinalization();
System.out.println(rq.poll() == pr); //取出引用队列中最先进入队列中的引用于pr进行比较 ②
}
}
输出结果为:true