二 、JVM (Java對象、對象引用類型)

目錄

 

Java對象的大小

測試

對象引用類型

強引用

軟引用

弱引用

虛引用 


Java對象的大小

這裏要使用的是lucene提供的專門用於計算堆內存佔用大小的工具類:RamUsageEstimator

<dependency>
      <groupId>org.apache.lucene</groupId>
      <artifactId>lucene-core</artifactId>
      <version>4.0.0</version>
</dependency>

使用該第三方工具比較簡單直接,主要依靠JVM本身環境、參數及CPU架構計算頭信息,再依據數據類型的標準計算實例字段大小,計算速度很快,另外使用較方便。如果非要說這種方式有什麼缺點的話,那就是這種方式計算所得的對象頭大小是基於JVM聲明規範的,並不是通過運行時內存地址計算而得,存在與實際大小不符的這種可能性。
常用API

//計算指定對象及其引用樹上的所有對象的綜合大小,單位字節
long RamUsageEstimator.sizeOf(Object obj)
 
//計算指定對象本身在堆空間的大小,單位字節
long RamUsageEstimator.shallowSizeOf(Object obj)
 
//計算指定對象及其引用樹上的所有對象的綜合大小,返回可讀的結果,如:2KB
 
String RamUsageEstimator.humanSizeOf(Object obj)

測試

    public static void main(String[] args) {
        Object o = new Object();
        //計算指定對象及其引用樹上的所有對象的綜合大小,單位字節
        System.out.println(RamUsageEstimator.sizeOf(o));
        //計算指定對象本身在堆空間的大小,單位字節
        System.out.println(RamUsageEstimator.shallowSizeOf(o));
        //計算指定對象及其引用樹上的所有對象的綜合大小,返回可讀的結果,如:2KB
        System.out.println(RamUsageEstimator.humanSizeOf(o));

        System.out.println("一個Interger對象大小爲:"+RamUsageEstimator.sizeOf(new Integer(1)));
        System.out.println("一個String對象大小爲:"+RamUsageEstimator.sizeOf(new String("a")));
        System.out.println("一個char對象大小爲:"+RamUsageEstimator.sizeOf(new char[1]));
        System.out.println("一個ArrayList對象大小爲:"+RamUsageEstimator.sizeOf(new ArrayList<>()));
        System.out.println("一個Object對象大小爲:"+RamUsageEstimator.sizeOf(new Object()));
        System.out.println("一個Long對象大小爲:"+RamUsageEstimator.sizeOf(new Long(10000000000L)));

    }


結果:
16
16
16 bytes
一個Interger對象大小爲:16
一個String對象大小爲:48
一個char對象大小爲:24
一個ArrayList對象大小爲:40
一個Object對象大小爲:16
一個Long對象大小爲:24

對象引用類型

從高到低依次:

  • 強引用(StrongReference)
  • 軟引用(SoftReference)
  • 弱引用(WeakReference)
  • 虛引用(PlantomReference)

 

強引用

就是我們一般聲明對象是時虛擬機生成的引用,強引用環境下,JVM寧可拋出OOM(out of memory)異常也不會回收強引用所指向的對象,即GC(垃圾回收或垃圾收集)絕對不會回收強引用類型。由於GC絕不會回收強引用,所以它將可能導致內存泄漏。

例:

     //object和str都是強引用
     Object object = new Object();
     String str = "hello";
     String s = new String()

軟引用

軟引用用於描述一些還有用但並非必須的對象,一般被做爲緩存來使用(比如網頁緩存、圖片緩存)。與強引用的區別是,軟引用在垃圾回收時,虛擬機會根據當前系統的剩餘內存來決定是否對軟引用進行回收。如果剩餘內存比較緊張,則虛擬機會回收軟引用所引用的空間;如果剩餘內存相對富裕,則不會進行回收。換句話說,虛擬機在發生OutOfMemory時,肯定是沒有軟引用存在的。

例:

        SoftReference<String> sr = new SoftReference(new String("123"));
        System.out.println(sr.get());

注意

在垃圾回收器對這個Java對象回收前,SoftReference類所提供的get方法會返回Java對象的強引用,一旦垃圾線程回收該Java對象之後,get方法將返回null。所以在獲取軟引用對象的代碼中,一定要判斷是否爲null,以免出現NullPointerException異常導致應用崩潰。

弱引用

弱引用用於實現一些規範化映射(WeakHashMap),其中key或者value當它們不再被引用時可以自動被回收。當你想引用一個對象,但是這個對象有自己的生命週期,你又不想介入這個對象的生命週期時,便可以用弱引用。同時弱引用也可以用來保存那些可有可無的緩存數據,和軟引用一樣,在內存充足時加速系統,在內存不足時便被系統回收。弱引用在系統GC時,只要一被發現,不管系統堆內存空間是否足夠,都會將對象進行回收,一旦被回收便會被加入到相關聯的引用隊列中。但是,由於垃圾回收器的線程通常優先級很低,所以並不一定能很快發現持有弱引用的對象,所以弱引用的生命週期是從被創建到下一次GC發送之前。
例:

        WeakReference<String> wr = new WeakReference(new String("123"));
        System.out.println(wr.get());
        System.gc();                //通知JVM的gc進行垃圾回收
        System.out.println(wr.get());

注意

第二個輸出結果是null,這說明只要JVM進行垃圾回收,被弱引用關聯的對象必定會被回收掉。不過要注意的是,這裏所說的被弱引用關聯的對象是指只有弱引用與之關聯,如果存在強引用同時與之關聯,則進行垃圾回收時也不會回收該對象(軟引用也是如此)。

虛引用 

虛引用又叫幽靈或幻影引用,主要用來跟蹤對象被垃圾回收器回收清理的活動,提供比Java清理機制更靈活的處理方式。虛引用是所有引用類型中最弱的一個,如果一個對象擁有一個虛引用,那它便和沒有被引用一樣,隨時可能被GC回收,而且GC在回收時會直接銷燬持有該引用的對象,並把虛引用加入引用隊列中。除此之外需要注意的一點是,當虛引用試圖通過get()方法取得強引用時,無論強引用是否存在,總是會失敗,即獲得的返回值永遠爲null。

虛引用與軟引用和弱引用的區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。
例:將虛引用和引用隊列結合使用,可以看到虛引用的對象被垃圾回收後,虛引用將被添加到引用隊列

        String str =new String("123");
        ReferenceQueue rq=new ReferenceQueue();
        WeakReference wr=new WeakReference(str);
        PhantomReference pr=new PhantomReference(str,rq);
        Object obj=wr.get();
        System.out.println(obj);
        System.out.println(wr.get());
        str=null;
        System.out.println(pr.get());
        System.gc();
        System.runFinalization();
        System.out.println(rq.poll()==pr);
        //rq.poll()函數作用爲檢索並移除文件的頭
        //rq.poll();
        System.out.println(rq.poll()==pr);

結果:
123
123
null
false
false

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章