欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java中的弱引用实例讲解

程序员文章站 2024-02-11 23:02:52
...

结论:只要有强引用指向的对象,都不会被回收。如果没有强引用指向,只是有弱引用指向的对象,在垃圾回收的时候,对象仍然会在垃圾回收时被回收。

最后代码可在jdk8上直接运行测试

在学习java反射机制的时候,我们会发现,java.lang.ref Reference是抽象类,有三个实现类。

Java中的弱引用实例讲解

在线api:http://tool.oschina.net/apidocs/apidoc?api=jd

Jvm虚拟机中,我们学习到引用有四类:

A.     强引用 B.软引用 C.弱引用 D.虚引用(也叫精灵引用)

我们现在主要讲弱引用:WeakReference。以及使用场合。

理论铺垫

1.首先引用是栈中的,它的内容是堆中的对象地址。引用指向的就是堆中的对象。如下图:

其中大写字母代表类,小写字母代表对象。下划线表示类中的属性。

 

             栈                                                                                                                  堆


 Java中的弱引用实例讲解


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;        

      

 Java中的弱引用实例讲解

 

        //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回收的时候,是不会被回收的,造成了内存泄漏)

 

 Java中的弱引用实例讲解


        

       

    }

 Java中的弱引用实例讲解


此时没有任何引用指向堆中对象a,但是a仍然存在,没有被回收。堆中的a对象,变成一块无法被垃圾回收器回收的对象,当遗留此类对象增多,就会导致内存溢出,只能重启jvm,释放堆内存。

那么我在B中要引用A中的对象,又不想造成这种情况,怎么办?

解决办法,就是把B中指向A的对象的引用处理成虚引用

B中增加 引用

public WeakReference<A>weak_a;

 

 Java中的弱引用实例讲解

下面分步骤测试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);

  

 Java中的弱引用实例讲解

 


        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,引用指向的对象可以找到:" ); //未调用垃圾回收

 


 

 Java中的弱引用实例讲解

        }

        try {

            Thread.sleep(100);

            System.gc();

            Thread.sleep(100);

        } catch (InterruptedExceptione) {

            e.printStackTrace();

        }

 

        /**

         * JDK6关于WeakReferenceget方法的解释

         * 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的引用不再指向aB中通过弱引用指向A对象a,此时gc回收的时候,发现没有强引用它,只有弱引用指向它,仍然会回收堆中的对象。此时weak_a引用仍然存在,而且指向堆中的地址,但是该地址中已经没有对象存在了,所以不会出现A中的对象a,变成堆中遗留下的不可控对象,这样就防止了内存泄漏:");//引用指向对象在堆中已经找不到了,所以不会A中的对象a,不再是堆中遗留下的不可控对象,防止了内存泄漏

      

 Java中的弱引用实例讲解


Java中的弱引用实例讲解


   }

此时,堆中的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;
    }
}