Java中的弱引用实例讲解
结论:只要有强引用指向的对象,都不会被回收。如果没有强引用指向,只是有弱引用指向的对象,在垃圾回收的时候,对象仍然会在垃圾回收时被回收。
最后代码可在jdk8上直接运行测试
在学习java反射机制的时候,我们会发现,java.lang.ref Reference是抽象类,有三个实现类。
在线api:http://tool.oschina.net/apidocs/apidoc?api=jd
Jvm虚拟机中,我们学习到引用有四类:
A. 强引用 B.软引用 C.弱引用 D.虚引用(也叫精灵引用)
我们现在主要讲弱引用:WeakReference。以及使用场合。
理论铺垫
1.首先引用是栈中的,它的内容是堆中的对象地址。引用指向的就是堆中的对象。如下图:
其中大写字母代表类,小写字母代表对象。下划线表示类中的属性。
栈 堆
A, B类的结构如下:
class A {
private String name = "class-A";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class B {
public A B_a;
public WeakReference<A> weak_a;
private String name = "class-B";
public B(WeakReference<A> a) {
weak_a = a;
}
public B(A a) {
B_a = a;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2.我们学习到,gc垃圾回收的时候,强引用指向的对象,永远不会回收。
也就是说,如果A a =new A();
B b = new B(a);//其中a被赋值给B_a.
此时,有两个强引用指向堆中的同一个对象a.
此时暂时先不关注weak_a,只关注B_a这个引用。
publicstaticvoidtestCommon() {
A a =new A();
B b =new B(a);
System.out.println(a);
System.out.println(b.B_a);
a =null;
//b=null;
if (details) {
//强引用未调用gc时
System.out.println("============强引用未调用gc时======================");
System.out.println("指向a的引用已经取消:" + a); //此处A的引用被取消
System.out.println("B中引用到A对象a的引用仍然存在:" + b.B_a);//引用仍然存在
System.out.println(b.B_a.getName()+":B中指向a的引用,仍然有对象存在。可以访问方法getName()" );//引用指向的对象a仍然存在(对象存在堆中,虽然栈中的引用不再指向它,但该对象在gc回收的时候,是不会被回收的,造成了内存泄漏)
}
try {
Thread.sleep(100);
System.gc();
Thread.sleep(100);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
if (details) {
System.out.println("============强引用调用gc后======================");
System.out.println("指向a的引用已经取消:" + a); //此处A的引用被取消
System.out.println("B中引用到A对象a的引用仍然存在:" + b.B_a);//引用仍然存在
}
System.out
.println(b.B_a.getName()+":B中指向a的引用,仍然有对象存在。可以访问方法getName()。对象存在堆中,虽然栈中A的引用不再指向它,但该B中的强引用B_a强引用仍然指向它,所以对象在gc回收的时候,是不会被回收的,造成了内存泄漏"
);//引用指向的对象a仍然存在(对象存在堆中,虽然栈中的引用不再指向它,但该对象在gc回收的时候,是不会被回收的,造成了内存泄漏)
}
此时没有任何引用指向堆中对象a,但是a仍然存在,没有被回收。堆中的a对象,变成一块无法被垃圾回收器回收的对象,当遗留此类对象增多,就会导致内存溢出,只能重启jvm,释放堆内存。
那么我在B中要引用A中的对象,又不想造成这种情况,怎么办?
解决办法,就是把B中指向A的对象的引用处理成虚引用
B中增加 引用
public WeakReference<A>weak_a;
下面分步骤测试gc回收 过程:
publicstaticvoidtestWeakRef() {
A a =new A();
WeakReference<A> weakA =new WeakReference<A>(a);
B b =new B(weakA);
System.out.println(a);
System.out.println(b.weak_a);
a =null;
//b = null;
if (details) {
System.out.println("---===========弱引用未调用gc时候===================----");
System.out.println(a);
System.out.println(b.weak_a);
System.out.println(b.weak_a.get()+":B中通过弱引用指向A对象a,引用指向的对象可以找到:" ); //未调用垃圾回收
}
try {
Thread.sleep(100);
System.gc();
Thread.sleep(100);
} catch (InterruptedExceptione) {
e.printStackTrace();
}
/**
* JDK6关于WeakReference的get方法的解释
* get
* public T get()
* 返回此引用对象的指示对象。如果此引用对象已经由程序或垃圾回收器清除,则此方法将返回 null。
* 返回:
* 此引用所引用的对象;如果此引用对象已经清除,则返回 null。
*
*/
if (details) {
System.out.println("---===========弱引用调用gc之后===================----");
System.out.println("A对象指向a的引用已经不存在:" + a);//a的引用也被取消
System.out.println("B中通过弱引用指向A对象a,引用仍然存在:" + b.weak_a);//引用仍然存在
}
System.out
.println(b.weak_a.get()
+ ":A的引用a指向对象在堆中已经找不到了.A的引用不再指向a,B中通过弱引用指向A对象a,此时gc回收的时候,发现没有强引用它,只有弱引用指向它,仍然会回收堆中的对象。此时weak_a引用仍然存在,而且指向堆中的地址,但是该地址中已经没有对象存在了,所以不会出现A中的对象a,变成堆中遗留下的不可控对象,这样就防止了内存泄漏:");//引用指向对象在堆中已经找不到了,所以不会A中的对象a,不再是堆中遗留下的不可控对象,防止了内存泄漏
}
此时,堆中的a对象的堆中内存空间,完全回收
结论:只要有强引用指向的对象,都不会被回收。如果没有强引用指向,只是有弱引用指向的对象,在垃圾回收的时候,对象仍然会被回收。
测试代码:
package com.maven.doc.plugin.docGenarate.refTest;
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static boolean details = false; //是否打印详细解释
public static void testCommon() {
A a = new A();
B b = new B(a);
System.out.println(a);
System.out.println(b.B_a);
a = null;
//b=null;
if (details) {
//强引用未调用gc时
System.out.println("============强引用未调用gc时======================");
System.out.println("指向a的引用已经取消:" + a); //此处A的引用被取消
System.out.println("B中引用到A对象a的引用仍然存在:" + b.B_a);//引用仍然存在
System.out.println(b.B_a.getName()+":B中指向a的引用,仍然有对象存在。可以访问方法getName()" );//引用指向的对象a仍然存在(对象存在堆中,虽然栈中的引用不再指向它,但该对象在gc回收的时候,是不会被回收的,造成了内存泄漏)
}
try {
Thread.sleep(100);
System.gc();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (details) {
System.out.println("============强引用调用gc后======================");
System.out.println("指向a的引用已经取消:" + a); //此处A的引用被取消
System.out.println("B中引用到A对象a的引用仍然存在:" + b.B_a);//引用仍然存在
}
System.out
.println(b.B_a.getName()+":B中指向a的引用,仍然有对象存在。可以访问方法getName()。对象存在堆中,虽然栈中A的引用不再指向它,但该B中的强引用B_a强引用仍然指向它,所以对象在gc回收的时候,是不会被回收的,造成了内存泄漏"
);//引用指向的对象a仍然存在(对象存在堆中,虽然栈中的引用不再指向它,但该对象在gc回收的时候,是不会被回收的,造成了内存泄漏)
b = null;//将B的引用指向空,此时再调gc垃圾回收,只会回收B的对象b,会将B_a指向置为空,但不会清理B_a指向的堆中的对象
//如何验证?
}
public static void testWeakRef() {
A a = new A();
WeakReference<A> weakA = new WeakReference<A>(a);
B b = new B(weakA);
System.out.println(a);
System.out.println(b.weak_a);
a = null;
//b = null;
if (details) {
System.out.println("---===========弱引用未调用gc时候===================----");
System.out.println(a);
System.out.println(b.weak_a);
System.out.println(b.weak_a.get()+":B中通过弱引用指向A对象a,引用指向的对象可以找到:" ); //未调用垃圾回收
}
try {
Thread.sleep(100);
System.gc();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* JDK6关于WeakReference的get方法的解释
* get
* public T get()
* 返回此引用对象的指示对象。如果此引用对象已经由程序或垃圾回收器清除,则此方法将返回 null。
* 返回:
* 此引用所引用的对象;如果此引用对象已经清除,则返回 null。
*
*/
if (details) {
System.out.println("---===========弱引用调用gc之后===================----");
System.out.println("A对象指向a的引用已经不存在:" + a);//a的引用也被取消
System.out.println("B中通过弱引用指向A对象a,引用仍然存在:" + b.weak_a); // 引用仍然存在
}
System.out
.println(b.weak_a.get()
+ ":A的引用a指向对象在堆中已经找不到了.A的引用不再指向a,B中通过弱引用指向A对象a,此时gc回收的时候,发现没有强引用它,只有弱引用指向它,仍然会回收堆中的对象。此时weak_a引用仍然存在,而且指向堆中的地址,但是该地址中已经没有对象存在了,所以不会出现A中的对象a,变成堆中遗留下的不可控对象,这样就防止了内存泄漏:"); //引用指向对象在堆中已经找不到了,所以不会A中的对象a,不再是堆中遗留下的不可控对象,防止了内存泄漏
// b = null;
//此时,b指向a的weak_a,由于已经不存在了。gc之后,不会在堆中产生不可控对象a。提前已经回收掉了。
}
public static void main(String[] args) {
details = true;//如果想要查看详细步骤,请放开该行代码
testCommon();
System.out.println();
System.out.println("----------------------------------");
System.out.println();
testWeakRef();
}
}
class A {
private String name = "class-A";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class B {
public A B_a;
public WeakReference<A> weak_a;
private String name = "class-B";
public B(WeakReference<A> a) {
weak_a = a;
}
public B(A a) {
B_a = a;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
上一篇: 请教DWZ PHP网站后台换页问题
下一篇: 堆和二叉堆的实现和特性