JAVA中的引用

  關於值類型和引用類型的話題,C++、JAVA、python、go、C#等等高級語言都有相關的概念,只要理解了其底層工做原理,能夠說即便是不一樣的語言,在面試學習工做實踐中均可以信手拈來(不要太糾集語言),固然此處我選擇了JAVA,雖然我是搞C++的,具體緣由都懂就不廢話了。java

1、值類型與引用類型python

  一、變量初始化面試

int num=10;
String str="hello"

  

  二、變量賦值
   從上圖能夠顯而易見,num是int基本類型變量,值就直接保存在變量中。str是String引用類型變量,變量中保存的只是實際對象對應的地址信息,而不是實際對象數據。對於而這特性,以下:數組

num=20; str="java";

  對於基本類型變量num,賦值運算符將會直接修改變量的值,原來的數據將被覆蓋掉,被替換爲新的值。對於引用類型變量str,賦值運算符只會改變變量中所保存的對象的地址信息,原來對象的地址被覆蓋掉,從新寫入新對象的地址數據。但原來的對象自己並不會被改變,只是再也不被任何引用所指向的對象,即「垃圾對象」,後續會被垃圾回收器回收。緩存

  三、函數傳參app

    // 基本類型參數,原始value不會被更改
    public void func(int value) { value = 100; } // 對於沒有提供修改自身的成員方法引用類型,原始str不會被更改
    public void func(String str) { str = "hello"; } StringBuilder sb = new StringBuilder("test"); // 對於提供修改自身的成員方法引用類型,原始的sBuilder會被更改
    public void func(StringBuilder sBuilder) { sBuilder.append("aa"); } // 原始的sBuilder不會被更改
    public void test(StringBuilder sBuilder) { sBuilder = new StringBuilder("111"); }

說明:對於第三種狀況:jvm

對於第四種狀況:函數

 

 

2、數據存儲方式學習

  一、局部變量/方法參數ui

  對於局部變量和方法傳遞的參數在jvm中的存儲方式是相同的,都是存儲在棧上開闢的空間中。方法參數空間在進入方法時開闢,方法退出時進行回收。以32爲JVM爲例,boolean、byte、short、char、int、float以及對應的引用類型都是分配4字節大小的空間,long、double分配8字節大小空間。對於每個方法來講,最多佔用空間大小是固定的,在編譯時就已經肯定了。當在方法中聲明一個int變量i=0或Object變量obj=null時,此時僅僅在棧上分配空間,不影響到堆空間。當new Object()時,將會在堆中開闢一段內存空間並初始化Object對象。

  二、數組類型引用和對象

  當聲明數組時,int[]  arr=new int[2];數組也是對象,arr其實是引用,棧上佔用4個字節大小的存儲空間,而是會在堆中開闢相應大小空間進行存儲,而後arr變量指向它。當聲明一個二維數組時,如:int[][]  arr2=new int[2][4],arr2一樣在棧中佔用4個字節,在堆內存中開闢長度爲2,類型爲int[]的數組對象,而後arr2指向這個數組。這個數組內部有兩個引用類型(大小爲4個字節),分別指向兩個長度爲4類型爲int的數組。內存分佈如圖:

因此當傳遞一個數組給一個方法時,數組的元素在方法內部是能夠被修改的,可是沒法讓數組引用指向新的數組。其實,還能夠聲明:int [][]  arr3=new int[3][],內存分佈以下:

  三、String類型數據

  對於String類型,其對象內部須要維護三個成員變量,char[]  chars,int  startIndex,  int  length。chars是存儲字符串數據的真正位置,在某些狀況下是能夠共用的,實際上String類型是不可變類型。例如:String  str=new String("hello"),內存分佈以下:

 

3、JAVA引用類型

  在JAVA中提供了四種引用類型:強引用、軟引用、軟引用和虛引用。在四種引用類型中,只有強引用FinalReference類型變量是包內可見的,其餘三種引用類型均爲public,能夠在程序中直接使用。

  一、強引用

  強引用是使用最廣泛的引用。若是一個對象具備強引用,那麼垃圾回收器毫不會回收它。例如:StringBuilder sb = new StringBuilder("test");變量str指向StringBuffer實例所在的堆空間,經過str能夠操做該對象。以下:

  強引用特色:

    • 強引用能夠直接訪問目標對象
    • 只要有引用變量存在,垃圾回收器永遠不會回收。JVM即便拋出OOM異常,也不會回收強引用所指向的對象。
    • 強引用可能致使內存泄漏問

   二、軟引用

  軟引用是除了強引用外,最強的引用類型。能夠經過java.lang.ref.SoftReference使用軟引用。一個持有軟引用的對象,不會被JVM很快回收,JVM會根據當前堆的使用狀況來判斷什麼時候回收。當堆使用率臨近閾值時,纔會去回收軟引用的對象。所以,軟引用能夠用於實現對內存敏感的高速緩存。SoftReference的特色是它的一個實例保存對一個Java對象的軟引用,該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference保存了對一個Java對象的軟引用後,在垃圾線程對 這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。一旦垃圾線程回收該Java對象以後,get()方法將返回null。

Object obj = new Object(); SoftReference<Object> sf = new SoftReference<Object>(obj); obj = null; sf.get();//有時候會返回null
sf是對obj的一個軟引用,經過sf.get()方法能夠取到這個對象,當這個對象被標記爲須要回收的對象時,則返回null;

  軟引用主要用戶實現相似緩存的功能,在內存足夠的狀況下直接經過軟引用取值,無需從繁忙的真實來源查詢數據,提高速度;當內存不足時,自動刪除這部分緩存數據,從真正的來源查詢這些數據。使用軟引用能防止內存泄露,加強程序的健壯性。軟引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。也就是說,ReferenceQueue中保存的對象是Reference對象,並且是已經失去了它所軟引用的對象的Reference對象。當調用它的poll()方法的時候,若是這個隊列中不是空隊列,那麼將返回隊列前面的那個Reference對象。在任什麼時候候,均可以調用ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及對象被回收。若是隊列爲空,將返回一個null,不然該方法返回隊列中前面的一個Reference對象。利用這個方法,能夠檢查哪一個SoftReference所軟引用的對象已經被回收,因而能夠把這些失去所軟引用的對象的SoftReference對象清除掉。

  三、弱引用

  弱引用是一種比軟引用較弱的引用類型。在系統GC時,只要發現弱引用,無論系統堆空間是否足夠,都會將對象進行回收。在java中,能夠用java.lang.ref.WeakReference實例來保存對一個Java對象的弱引用。弱引用與軟引用的區別在於:只具備弱引用的對象擁有更短暫的生命週期。不過,因爲垃圾回收器是一個優先級很低的線程,所以不必定會很快發現那些只具備弱引用的對象。弱引用主要用於監控對象是否已經被垃圾回收器標記爲即將回收的垃圾,能夠經過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記。弱引用能夠和一個引用隊列(ReferenceQueue)聯合使用,若是弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。

Object obj = new Object(); WeakReference<Object> wf = new WeakReference<Object>(obj); obj = null; wf.get();//有時候會返回null
wf.isEnQueued();//返回是否被垃圾回收器標記爲即將回收的垃圾

  四、虛引用

  虛引用是全部類型中最弱的一個。一個持有虛引用的對象和沒有引用幾乎是同樣的,隨時可能被垃圾回收器回收,當試圖經過虛引用的get()方法取得強引用時,老是會失敗。而且虛引用必須和引用隊列一塊兒使用,它的做用在於檢測對象是否已經從內存中刪除,跟蹤垃圾回收過程。當垃圾回收器準備回收一個對象時,若是發現它還有虛引用,就會在垃圾回收後,銷燬這個對象,將這個虛引用加入引用隊列。程序能夠經過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動。

Object obj = new Object(); PhantomReference<Object> pf = new PhantomReference<Object>(obj); obj=null; pf.get();//永遠返回null
pf.isEnQueued();//返回是否從內存中已經刪除

 

@Test
    public void test(){
        Map map;
        map = new WeakHashMap<String,Object>();
        for (int i =0;i<10000;i++){
            map.put("key"+i,new byte[i]);
        }
//        map = new HashMap<String,Object>();
//        for (int i =0;i<10000;i++){
//            map.put("key"+i,new byte[i]);
//        }
    }

  使用-Xmx2M限定堆內存,使用WeakHashMap的代碼正常運行結束,而使用HashMap的代碼段拋出異常:java.lang.OutOfMemoryError: Java heap space。因而可知,WeakHashMap會在系統內存緊張時使用弱引用,自動釋放掉持有弱引用的內存數據。但若是WeakHashMap的key都在系統內持有強引用,那麼WeakHashMap就退化爲普通的HashMap,由於全部的數據項都沒法被自動清理。

相關文章
相關標籤/搜索