目錄
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