程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java/Android援用類型及其應用周全剖析

Java/Android援用類型及其應用周全剖析

編輯:關於JAVA

Java/Android援用類型及其應用周全剖析。本站提示廣大學習愛好者:(Java/Android援用類型及其應用周全剖析)文章只能為提供參考,不一定能成為您想要的結果。以下是Java/Android援用類型及其應用周全剖析正文


Java/Android中有四種援用類型,分離是:

Strong reference - 強援用
Soft Reference - 軟援用
Weak Reference - 弱援用
Phantom Reference - 虛援用

分歧的援用類型有著分歧的特征,同時也對應著分歧的應用場景。

1.Strong reference - 強援用

現實編碼中最多見的一種援用類型。罕見情勢如:A a = new A();等。強援用自己存儲在棧內存中,其存儲指向對內存中對象的地址。普通情形下,當對內存中的對象不再有任何強援用指向它時,渣滓收受接管機械開端斟酌能夠要對此內存停止的渣滓收受接管。如當停止編碼:a = null,此時,方才在堆平分配地址並新建的a對象沒有其他的任何援用,當體系停止渣滓收受接管時,堆內存將被渣滓收受接管。

SoftReference、WeakReference、PhantomReference都是類java.lang.ref.Reference的子類。Reference作為籠統基類,界說了其子類對象的根本操作。Reference子類都具有以下特色:

1.Reference子類不克不及無參化直接創立,必需至多以強援用對象為結構參數,創立各自的子類對象;
2.由於1中以強援用對象為結構參數創立對象,是以,使得本來強援用所指向的堆內存中的對象將不再只與強援用自己直接聯系關系,與Reference的子類對象的援用也有必定接洽。且此種接洽將能夠影響到對象的渣滓收受接管。

依據分歧的子類對象對其指導對象(強援用所指向的堆內存中的對象)的渣滓收受接管分歧的影響特色,分離構成了三個子類,即SoftReference、WeakReference和PhantomReference。

2.Soft Reference - 軟援用

軟援用的普通應用情勢以下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);

經由過程對象的強援用為參數,創立了一個SoftReference對象,並使棧內存中的wrA指向此對象。

此時,停止以下編碼:a = null,關於本來a所指向的A對象的渣滓收受接管有甚麼影響呢?

先直接看一下上面一段法式的輸入成果:

import java.lang.ref.SoftReference;

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();
    
    SoftReference<A> srA = new SoftReference<A>(a);

    a = null;

    if (srA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + srA.get());
    }

    // 渣滓收受接管
    System.gc();

    if (srA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + srA.get());
    }

  }
}

class A {

}

##輸入成果為:

1 a對象還沒有被收受接管A@4807ccf6
2 a對象還沒有被收受接管A@4807ccf6

當 a = null後,堆內存中的A對象將不再有任何的強援用指向它,但此時髦存在srA援用的對象指向A對象。當第一次挪用srA.get()辦法前往此指導對象時,因為渣滓收受接管器很有能夠還沒有停止渣滓收受接管,此時get()是有成果的,這個很好懂得。當法式履行System.gc();強迫渣滓收受接管後,經由過程srA.get(),發明仍然可以獲得所指導的A對象,解釋A對象並未被渣滓收受接管。那末,軟援用所指導的對象甚麼時刻才開端被渣滓收受接管呢?須要知足以下兩個前提:

1.當其指導的對象沒有任何強援用對象指向它;

2.當虛擬機內存缺乏時。

是以,SoftReference變相的延伸了其指導對象占領堆內存的時光,直到虛擬機內存缺乏時渣滓收受接管器才收受接管此堆內存空間。

3.Weak Reference - 弱援用

異樣的,軟援用的普通應用情勢以下:

A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);

當沒有任何強援用指向此對象時, 其渣滓收受接管又具有甚麼特征呢?

import java.lang.ref.WeakReference;

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    WeakReference<A> wrA = new WeakReference<A>(a);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    // 渣滓收受接管
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

  }

}

class A {

}

##輸入成果為:

a對象還沒有被收受接管A@52e5376a
a對象進入渣滓收受接管流程

輸入的第一條成果說明同上。當停止渣滓收受接管後,wrA.get()將前往null,注解其指導對象進入到了渣滓收受接管進程中。是以,對弱援用特色總結為:

WeakReference不轉變原有強援用對象的渣滓收受接管機會,一旦其指導對象沒有任何強援用對象時,此對象即進入正常的渣滓收受接管流程。

那末,根據此特色,極可能有疑問:WeakReference存在又有甚麼意義呢?

其重要應用場景見於:以後已有強援用指向強援用對象,此時因為營業須要,須要增長對此對象的援用,同時又不願望轉變此援用的渣滓收受接管機會,此時WeakReference正好相符需求,罕見於一些與性命周期的場景中。

上面給出一個Android中關於WeakReference應用的場景 —— 聯合靜態外部類和WeakReference來處理Activity中能夠存在的Handler內存洩漏成績。

Activity中我們須要新建一個線程獲得數據,應用handler - sendMessage方法。上面是這一進程的普通性代碼:

public class MainActivity extends Activity {

  //...
  private int page;
  private Handler handler = new Handler() {

    @Override
    public void handleMessage(Message msg) {
      if (msg.what == 1) {

        //...

        page++;
      } else {

        //...

      }

    };
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //...

    new Thread(new Runnable() {
      @Override
      public void run() {
        //.. 
        Message msg = Message.obtain();
        msg.what = 1;
        //msg.obj = xx;
        handler.sendMessage(msg);
      }
    }).start();

    //...

  }

}

在Eclispe中Run Link,將會看到警示信息:This Handler class should be static or leaks might occur ...點擊檢查此信息,其概況中對成績停止了解釋並給出了建議性的處理計劃。

Issue: Ensures that Handler classes do not hold on to a reference to an outer class
Id: HandlerLeak

Since this Handler is declared as an inner class, it may prevent the outer class from being garbage collected. If the Handler is using a Looper or MessageQueue for a thread other than the main thread, then there is no issue. If the Handler is using the Looper or MessageQueue of the main thread, you need to fix your Handler declaration, as follows: Declare the Handler as a static class;In the outer class, instantiate a WeakReference to the outer class and pass this object to your Handler when you instantiate the Handler; Make all references to members of the outer class using the WeakReference object.

年夜致的意思是建議將Handler界說成外部靜態類,並在此靜態外部類中界說一個WeakReference的援用,因為指導內部的Activity對象。

成績剖析:

Activity具有本身的性命周期,Activity中新開啟的線程運轉進程中,能夠此時用戶按下了Back鍵,或體系內存缺乏等願望收受接管此Activity,因為Activity中新起的線程其實不會遵守Activity自己的甚麼周期,也就是說,當Activity履行了onDestroy,因為線程和Handler 的HandleMessage的存在,使得體系本願望停止此Activity內存收受接管不克不及完成,由於非靜態外部類中隱性的持有對內部類的援用,招致能夠存在的內存洩漏成績。

是以,在Activity中應用Handler時,一方面須要將其界說為靜態外部類情勢,如許可使其與內部類(Activity)解耦,不再持有內部類的援用,同時因為Handler中的handlerMessage普通都邑若干須要拜訪或修正Activity的屬性,此時,須要在Handler外部界說指向此Activity的WeakReference,使其不會影響到Activity的內存收受接管同時,可以在正常情形下拜訪到Activity的屬性。

Google官方給出的建議寫法為:

public class MainActivity extends Activity {

  //...
  private int page;
  private MyHandler mMyHandler = new MyHandler(this);

  private static class MyHandler extends Handler {

    private WeakReference<MainActivity> wrActivity;

    public MyHandler(MainActivity activity) {
      this.wrActivity = new WeakReference<MainActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      if (wrActivity.get() == null) {
        return;
      }
      MainActivity mActivity = wrActivity.get();
      if (msg.what == 1) {

        //...
        mActivity.page++;

      } else {

        //...

      }
    }

  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //...

    new Thread(new Runnable() {
      @Override
      public void run() {
        //.. 
        Message msg = Message.obtain();
        msg.what = 1;
        //msg.obj = xx;
        mMyHandler.sendMessage(msg);
      }
    }).start();

    //...

  }

}

關於SoftReference和WeakReference,還有一個結構器參數為ReferenceQueue<T>,當SoftReference或WeakReference所指導的對象確切被渣滓收受接管後,其援用將被放置於ReferenceQueue中。留意上文中,當SoftReference或WeakReference的get()辦法前往null時,僅是注解其指導的對象曾經進入渣滓收受接管流程,此時對象紛歧定曾經被渣滓收受接管。而只要確認被渣滓收受接管後,假如ReferenceQueue,其援用才會被放置於ReferenceQueue中。

看上面的一個例子:

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    WeakReference<A> wrA = new WeakReference<A>(a);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    // 渣滓收受接管
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}

##輸入成果為:

1 a對象還沒有被收受接管A@46993aaa
2 a對象被收受接管
3 in A finalize

由此,也驗證了上文中的“進入渣滓收受接管流程”的說法。上面聯合ReferenceQueue,看一段代碼:

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    WeakReference<A> wrA = new WeakReference<A>(a, rq);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    System.out.println("rq item:" + rq.poll());

    // 渣滓收受接管
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    /*
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    */

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}

##輸入成果為:

a對象還沒有被收受接管A@302b2c81
rq item:null
a對象進入渣滓收受接管流程
rq item:null
in A finalize

由此,驗證了“僅進入渣滓收受接管流程的SoftReference或WeakReference援用還沒有被參加到ReferenceQueue”。


public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    WeakReference<A> wrA = new WeakReference<A>(a, rq);

    a = null;

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    System.out.println("rq item:" + rq.poll());

    // 渣滓收受接管
    System.gc();

    if (wrA.get() == null) {
      System.out.println("a對象進入渣滓收受接管流程");
    } else {
      System.out.println("a對象還沒有被收受接管" + wrA.get());
    }

    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("in A finalize");
  }

}

##輸入成果為:

a對象還沒有被收受接管A@6276e1db
rq item:null
a對象進入渣滓收受接管流程
in A finalize
rq item:java.lang.ref.WeakReference@645064f

由此,證明了上陳述法。

4.PhantomReference

與SoftReference或WeakReference比擬,PhantomReference重要差異表現在以下幾點:

1.PhantomReference只要一個結構函數PhantomReference(T referent, ReferenceQueue<? super T> q),是以,PhantomReference應用必需聯合ReferenceQueue;

2.不論有沒有強援用指向PhantomReference的指導對象,PhantomReference的get()辦法前往成果都是null。

public class ReferenceTest {

  public static void main(String[] args) {

    A a = new A();

    ReferenceQueue<A> rq = new ReferenceQueue<A>();
    PhantomReference<A> prA = new PhantomReference<A>(a, rq);

    System.out.println("prA.get():" + prA.get());
    
    a = null;
    
    System.gc();
    
    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    System.out.println("rq item:" + rq.poll());

  }
}

class A {

}

##輸入成果為:

prA.get():null
rq item:java.lang.ref.PhantomReference@1da12fc0

代碼中的Thread.sleep(1);感化與上例中雷同,都是確保渣滓收受接管線程可以或許履行。不然,進進入渣滓收受接管流程而沒有真正被渣滓收受接管的指導對象的虛援用是不會被參加到PhantomReference中的。

與WeakReference雷同,PhantomReference其實不會轉變其指導對象的渣滓收受接管機會。且可以總結出:ReferenceQueue的感化重要是用於監聽SoftReference/WeakReference/PhantomReference的指導對象能否曾經被渣滓收受接管。

以上就是小編為年夜家帶來的Java/Android援用類型及其應用周全剖析的全體內容了,願望對年夜家有所贊助,多多支撐~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved