一. 強引用、軟引用、弱引用和虛引用
- 強引用:使用最普遍的引用,一般情況下,垃圾回收器絕對不會回收它。內(nèi)存不足時,拋出OOM。
String s = new String("hello");
s = null; //不加該行,會輸出hello
System.gc(); //垃圾回收
System.out.println(s);
輸出結(jié)果:null
對象只有在創(chuàng)建它的方法執(zhí)行結(jié)束才會被回收,或者主動設置obj = null。
- 軟引用:內(nèi)存空間足夠,垃圾回收器不會回收它。反之,則回收。適用于緩存,而且不會OOM。
SoftReference<Object[]> reference = new SoftReference<>(new Object[300000000]);
System.out.println(reference.get());
Object[] objects = new Object[100000000];// 3
System.out.println(reference.get());
輸出結(jié)果:
[Ljava.lang.Object;@4554617c
null
結(jié)果說明執(zhí)行代碼3時,內(nèi)存不夠,垃圾回收器主動回收了軟引用指向的對象。
PS:Object數(shù)組長度根據(jù)JVM配置不同而不同。
- 弱引用:只有當垃圾回收器掃描到弱引用指向的對象時,才會回收它。生命周期比軟引用更短。
ThreadLocal的key使用了弱引用。
WeakReference<String> reference = new WeakReference<>(new String("hello"));
System.out.println(reference.get());
System.gc(); //垃圾回收
System.out.println(reference.get());
輸出結(jié)果:
hello
null
- 虛引用:在任何時候都可能被垃圾回收器回收,必須與引用隊列關(guān)聯(lián)使用。
ReferenceQueue<String> queue = new ReferenceQueue<>();
PhantomReference<String> reference = new PhantomReference<>(new String("hello"), queue);
System.out.println(reference.get());
輸出結(jié)果:
null
二. ThreadLocal
一個線程可以有多個ThreadLocal實例。其作用是存儲線程本地變量,獨享資源,避免和主內(nèi)存通信,從而提高效率。
原理:都是操作線程中的ThreadLocalMap對象。
set(obj): 獲取當前線程中的ThreadLocalMap,以ThreadLocal實例為key,obj為value,并且key是弱引用。也就是說ThreadLocal實例(未被其他變量強引用)被垃圾回收器掃描到就會被回收,從而導致key = null。這就是為什么get、set、remove的時候都會有清除Entry[]中key = null的數(shù)據(jù)的步驟。
remove():會把當前key、value、key-value所在位置Entry[i]置為null,以及清除Entry[]中從i到末尾上key = null的數(shù)據(jù)。
內(nèi)存泄露:key是弱引用,所以垃圾回收器會主動回收。但是value和Entry[i]都是強引用,只有線程銷毀的時候才會被回收。當線程長時間未被銷毀,或者線程池循環(huán)利用線程的時候,value和Entry[i]一直被強引用,不會被垃圾回收器回收,可能會造成OOM。
解決方法:使用完務必調(diào)用remove()方法,防止內(nèi)存泄漏。
以上。