title: 對(duì)象回收與引用類(lèi)型
date: 2017-03-22 22:16:45
tags:
- Java
categories: Java
引用分類(lèi)
- 強(qiáng)引用:日常使用的引用
- 軟引用:當(dāng)內(nèi)存空間不足時(shí)修己,不論是否引用都進(jìn)行回收的引用
- 弱引用:只要發(fā)生GC承璃,就被回收的引用
- 虛引用:“引用不到”的引用類(lèi)型
四個(gè)引用強(qiáng)度自上而下逐級(jí)遞減衙吩,一個(gè)對(duì)象存在多個(gè)引用時(shí)以最強(qiáng)的引用類(lèi)型為準(zhǔn);
除了以上四個(gè)引用類(lèi)型其實(shí)還有兩個(gè):FinalReference囱持,F(xiàn)inalizer
使用
SoftReference
非常適合拿來(lái)做緩存,實(shí)現(xiàn)類(lèi)似下面:
ReferenceQueue<Map<String, Object>> queue = new ReferenceQueue<>();
Map<String, Object> map = new HashMap<>();
SoftReference<Map<String, Object>> softReference = new SoftReference<>(map, queue);
Map<String, Object> cache = softReference.get();
// 使用cache
if (cache == null) {
cache = new HashMap();
}
// 清理
Reference reference;
while ((reference = queue.poll()) != null) {
clearn();
}
WeakReference
通常只要發(fā)生GC焕济,就會(huì)回收WeakReference引用的對(duì)象纷妆。
當(dāng)引用時(shí)常量是無(wú)法回收,還是可以通過(guò)弱引用獲取晴弃,例如:
@Test
public void weakReference() {
String string = new String("zhangh.tk");
WeakReference<String> reference = new WeakReference<>(string);
assert reference.get() != null;
string = null;
runFinalization();
assert reference.get() == null;
}
@Test
public void weakReferenceWithFinal() {
String finalStr = "zhangh.tk";
WeakReference<String> reference = new WeakReference<>(finalStr);
assert reference.get() != null;
finalStr = null;
runFinalization();
assert reference.get() != null;
}
典型的使用場(chǎng)景:
Thread實(shí)例中維護(hù)的ThreadLocalMap中key使用的是WeakReference<ThreadLocal<?>>
掩幢。
當(dāng)不存在對(duì)ThreadLocal的強(qiáng)引用時(shí),只有ThreadLocalMap對(duì)它存在弱引用上鞠,GC就可以回收ThreadLocal對(duì)象际邻。否則只要Thread對(duì)象存在,那么始終保持對(duì)ThreadLocal的引用芍阎,就不能回收ThreadLocal世曾,造成內(nèi)存泄露。
與上面ThreadLocalMap用法類(lèi)似谴咸,JDK提供了WeakHashMap度硝。
無(wú)論WeakHashMap還是ThreadLocalMap都存在當(dāng)key被回收,value仍然存在的問(wèn)題寿冕。
兩者解決此問(wèn)題的思路也很類(lèi)似蕊程,都是在類(lèi)似size,add等方法時(shí)清理key為null的value避免內(nèi)存泄露驼唱。
@Test
public void weakHashMapOOM() {
List<WeakHashMap<byte[][], byte[][]>> list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
WeakHashMap<byte[][], byte[][]> map = new WeakHashMap<>();
map.put(new byte[10000][1000], new byte[10000][1000]);
list.add(map);
runFinalization();
}
}
@Test
public void weakHashMapNotOOM() {
List<WeakHashMap<byte[][], byte[][]>> list = new ArrayList<>();
for (int i = 0; i < Integer.MAX_VALUE; i++) {
WeakHashMap<byte[][], byte[][]> map = new WeakHashMap<>();
map.put(new byte[10000][1000], new byte[10000][1000]);
list.add(map);
runFinalization();
map.size();
}
}
PhantomReference
說(shuō)它是引用不到的引用類(lèi)型是因?yàn)槭褂肞hantomReference無(wú)法獲取到指向的對(duì)象藻茂,如果引用有強(qiáng)度那他的引用強(qiáng)度實(shí)在是太弱了。
使用PhantomReference必須配合ReferenceQueue玫恳,關(guān)于ReferenceQueue后面再說(shuō)辨赐。
FinalReference&Finalizer
這兩個(gè)類(lèi)是父子關(guān)系,并且都是不公開(kāi)的京办。若一個(gè)對(duì)象沒(méi)有實(shí)現(xiàn)finalize方法掀序,可以直接被回收,若實(shí)現(xiàn)了finalize方法由Finalizer處理惭婿。
對(duì)象回收?qǐng)?zhí)行大致過(guò)程:
- 當(dāng)對(duì)象不可達(dá)時(shí)不恭,若未覆蓋finalize方法直接回收叶雹,否則加入F-Queue隊(duì)列
- FinalizerThread線程從F-Queue里面,不停的獲取數(shù)據(jù),然后調(diào)用相應(yīng)的finalize方法
- finalize方法完畢后,GC會(huì)再次判斷該對(duì)象是否可達(dá)换吧,若不可達(dá)折晦,則進(jìn)行回收,否則沾瓦,對(duì)象“復(fù)活”
關(guān)于對(duì)象復(fù)活:finalize方法只能被JVM調(diào)用一次满着,也就是最多兩條命,不存在不停復(fù)活的情況贯莺。
對(duì)象從創(chuàng)建到被回收狀態(tài)轉(zhuǎn)換:
對(duì)象復(fù)活示例:
class Reclaimable {
static Reclaimable staticVar;
@Override
protected void finalize() throws Throwable {
staticVar = this;
}
}
@Test
public void reclaimed() {
Reclaimable reclaimable = new Reclaimable();
reclaimable = null;
runFinalization();
assert Reclaimable.staticVar != null;
Reclaimable.staticVar = null;
runFinalization();
assert Reclaimable.staticVar == null;
}
引用隊(duì)列
引用隊(duì)列是垃圾收集器向應(yīng)用程序返回關(guān)于對(duì)象生命周期的信息的一種方式风喇。當(dāng)一個(gè)引用對(duì)象被回收后可以選擇加入引用隊(duì)列,做最后的清理工作缕探。
例如上文提到的WeakHashMap就是用引用隊(duì)列收集失效引用响驴,根據(jù)引用隊(duì)列數(shù)據(jù)清除Entry和值對(duì)象。
再例如撕蔼,因?yàn)椴荒軓腜hantomReference獲得引用的對(duì)象豁鲤,但是可以從引用隊(duì)列中獲得對(duì)象被回收的通知。
可以使用引用隊(duì)列和PhantomReference觀察對(duì)象的復(fù)活:
@Test
public void phantomQueueWithUnReclaimed() {
class A{}
ReferenceQueue<A> queue = new ReferenceQueue<>();
PhantomReference<A> reference = new PhantomReference<>(new A(), queue);
assert reference.get() == null;
assert queue.poll() == null;
runFinalization();
assert reference.get() == null;
assert queue.poll() != null;
}
@Test
public void phantomQueueWithReclaimed() {
ReferenceQueue<Reclaimable> queue = new ReferenceQueue<>();
PhantomReference<Reclaimable> reference = new PhantomReference<>(new Reclaimable(), queue);
assert reference.get() == null;
assert queue.poll() == null;
runFinalization();
assert reference.get() == null;
assert queue.poll() == null;
Reclaimable.staticVar = null;
runFinalization();
assert queue.poll() != null;
}