應(yīng)用計(jì)數(shù)器法沒(méi)辦法規(guī)避循環(huán)引用,忽略它。。。
跟搜索算法root節(jié)點(diǎn):
- 虛擬機(jī)棧,本地方法棧中引用的對(duì)象
- 方法區(qū)引用的類靜態(tài)變量(java8后已經(jīng)屬于堆了) // TODO 待確認(rèn)
- 方法區(qū)中的常量引用的對(duì)象(java8后已經(jīng)屬于堆了) // TODO 待確認(rèn)
java的強(qiáng)軟弱虛引用
- 強(qiáng)引用不多說(shuō),jvm無(wú)論如何都不會(huì)回收有強(qiáng)引用的對(duì)象
- 軟引用,內(nèi)存快溢出前會(huì)回收軟引用對(duì)象。(安卓端一些圖片緩存,全部放進(jìn)內(nèi)存又太大,從磁盤慢慢讀又太慢,那就用軟引用緩存)
- 弱引用,不確定的時(shí)間回收,垃圾收集器發(fā)現(xiàn)弱引用對(duì)象就會(huì)回收它。
WeakHashMap
就是弱引用的map實(shí)現(xiàn),它的key是關(guān)聯(lián)到一個(gè)軟引用的,一旦key在外面不存在引用了,會(huì)被自動(dòng)清除??梢詫eferenceQuene傳入WeakHashmap的構(gòu)造方法(constructor)中,這樣,一旦這個(gè)弱引用指向的對(duì)象成為垃圾,這個(gè)弱引用將加入ReferenceQuene??磦€(gè)例子:
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
public class Test {
public static void main(String[] args) throws Exception {
String a = new String("a");
String b = new String("b");
Map weakmap = new WeakHashMap();
Map map = new HashMap();
map.put(a, "aaa");
map.put(b, "bbb");
weakmap.put(a, "aaa");
weakmap.put(b, "bbb");
map.remove(a);
a=null;
b=null;
System.gc();
Iterator i = map.entrySet().iterator();
while (i.hasNext()) {
Map.Entry en = (Map.Entry)i.next();
System.out.println("map:"+en.getKey()+":"+en.getValue());
}
Iterator j = weakmap.entrySet().iterator();
while (j.hasNext()) {
Map.Entry en = (Map.Entry)j.next();
System.out.println("weakmap:"+en.getKey()+":"+en.getValue());
}
}
}
當(dāng)把a(bǔ)和b都置為null后,hashmap中也不存在對(duì)它的引用了(remove掉了),外邊a也被置null了,weakhashmap將會(huì)自動(dòng)移除a為key的記錄,b雖然被置為null了,但是new String("bbb")
出來(lái)的對(duì)象在hashmap中還存在引用,所以不會(huì)被回收。
(弱引用最大的用處是,你需要用一個(gè)對(duì)象,但也只是用一用,不能影響它的垃圾回收,比如需要監(jiān)控虛擬機(jī)中某些對(duì)象的屬性值,如果直接用強(qiáng)引用獲取,那么被監(jiān)控的對(duì)象將不會(huì)被gc回收,此時(shí)弱引用就派上了用場(chǎng))
- 虛引用,它唯一的作用就是跟
ReferenceQueue
一起使用,通知對(duì)象被回收。
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.reflect.Field;
public class Test {
public static boolean isRun = true;
@SuppressWarnings("static-access")
public static void main(String[] args) throws Exception {
String abc = new String("abc");
System.out.println(abc.getClass() + "@" + abc.hashCode());
final ReferenceQueue<String> referenceQueue = new ReferenceQueue<String>();
new Thread() {
public void run() {
while (isRun) {
Object obj = referenceQueue.poll();
if (obj != null) {
try {
Field rereferent = Reference.class
.getDeclaredField("referent");
rereferent.setAccessible(true);
Object result = rereferent.get(obj);
System.out.println("gc will collect:"
+ result.getClass() + "@"
+ result.hashCode() + "\t"
+ (String) result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}.start();
PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,
referenceQueue);
abc = null;
Thread.currentThread().sleep(3000);
System.gc();
Thread.currentThread().sleep(3000);
isRun = false;
}
}
我們可以聲明虛引用來(lái)引用我們感興趣的對(duì)象,在gc要回收的時(shí)候,gc收集器會(huì)把這個(gè)對(duì)象添加到referenceQueue(如上例中,abcWeakRef虛引用就對(duì)abs這個(gè)對(duì)象感興趣,abc被回收時(shí)會(huì)被放入referenceQueue中,我們只用監(jiān)聽(tīng)referenceQueue就知道哪些對(duì)象被回收了),這樣我們?nèi)绻麢z測(cè)到referenceQueue中有我們感興趣的對(duì)象的時(shí)候,說(shuō)明gc將要回收這個(gè)對(duì)象了。此時(shí)我們可以在gc回收之前做一些其他事情(如安卓圖片滾動(dòng)顯示,每次只顯示一張圖片,但是圖片特別大,內(nèi)存只能容得下一張圖片的大小,此時(shí)就需要虛引用來(lái)監(jiān)聽(tīng)前一張圖片是否已經(jīng)被移除,移除之后才能加載下一張圖片進(jìn)來(lái))
finalize方法
沒(méi)有實(shí)現(xiàn)finallize方法的對(duì)象不會(huì)執(zhí)行這一堆邏輯,對(duì)象回收時(shí)干的事情,執(zhí)行后對(duì)象會(huì)被放入F-queue中,如果此時(shí)對(duì)象又被引用上,對(duì)象將起死回生。每個(gè)對(duì)象虛擬機(jī)只會(huì)執(zhí)行一次finalize方法。建議對(duì)象回收監(jiān)聽(tīng)使用虛引用(幽靈引用)來(lái)實(shí)現(xiàn)。
回收方法區(qū)
- 回收常量池
某個(gè)字符串已經(jīng)沒(méi)有任何引用指向它時(shí)即可回收。
- 回收類條件
- 沒(méi)有任何類實(shí)例(全部被回收)
- 加載該類的類加載器已經(jīng)被回收
- 類的Class對(duì)象沒(méi)有在任何地方被引用(不能反射創(chuàng)建實(shí)例)
類回收相關(guān)虛擬機(jī)參數(shù):
- -verbose:class可以查看類加載信息
- -XX:+TraceClassLoading,-XX:+TraceClassUnLoading可以查看類的加載和卸載信息。
垃圾收集算法
- 標(biāo)記清除
- 復(fù)制
- 標(biāo)記整理
- 分代收集
垃圾收集器
Seria收集器(串行收集器,單線程,采用復(fù)制算法)年輕代收集器
ParNew(和Seria一樣,只是多線程版本而已)年輕帶收集器
SeriaOld 串行收集器,老年代版本,但是使用
標(biāo)記整理
算法。CMS并發(fā)老年代收集器。(標(biāo)記清除)
初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,并發(fā)清除。G1收集器:http://blog.csdn.net/renfufei/article/details/41897113 待學(xué)習(xí)
內(nèi)存分配回收策略
- 優(yōu)先分配到eden區(qū)域
參數(shù):-Xms20M -Xmx20M 堆最小值及最大值,-Xmn10M堆的新生代大小,設(shè)置為10MB后,老年代大小就為20m-10m=10m了。 -XX:SurvivorRatio=8代表survicor:eden=1:8,設(shè)置為8后,新生代中eden區(qū)域就占8/10,兩個(gè)survivor分別占1/10;
- 大對(duì)象(大數(shù)組或超長(zhǎng)字符串)直接進(jìn)入老年代
因?yàn)閷?duì)象太大先存入eden區(qū),再進(jìn)survivor進(jìn)行復(fù)制算法拷貝代價(jià)就會(huì)比較大。
可以使用虛擬機(jī)參數(shù)-XX:PretenureSzieThreshold:13723來(lái)設(shè)定判斷大對(duì)象的閾值(需要注意,這里單位默認(rèn)為b,寫的時(shí)候不能自定義單位,另外,只有parNew和Seria收集器會(huì)識(shí)別該參數(shù))
- 長(zhǎng)期存活對(duì)象進(jìn)入老年代
并發(fā)那一塊的筆記里對(duì)對(duì)象頭的介紹,除了偏向鎖等信息等之外就有一個(gè)age屬性,記錄對(duì)象的垃圾收集年齡,在survivor區(qū)每次被復(fù)制都會(huì)+1,虛擬機(jī)默認(rèn)達(dá)到15歲進(jìn)入老年代,可以手動(dòng)設(shè)置年齡閾值,-XX:MaxTenuringThreshold:5
tips:虛擬機(jī)有另外一個(gè)動(dòng)態(tài)年齡判斷規(guī)則,如果某個(gè)年齡的對(duì)象大小占據(jù)了survivor區(qū)的一半,則會(huì)將年齡大于等于該年齡的所有對(duì)象晉升到老年區(qū)。
- 空間分配擔(dān)保
擔(dān)保,就是年輕代沒(méi)有容納能力了,老年代替你存。(這里老年代替年輕代存的是新的需要分配空間的對(duì)象還是survivor區(qū)的最老的對(duì)象??????。。』卮?當(dāng)然是最老的對(duì)象了咯,除非是大對(duì)象)
?。】臻g分配擔(dān)保是一直都開(kāi)啟的,HandlePromotionFailure參數(shù)只是設(shè)置是否允許擔(dān)保失敗。
HandlePromotionFailure參數(shù)配置的影響:每次minorgc前先判斷老年代是否能容納全部當(dāng)前年輕代的大小,成立->直接minorgc即可,不成立-> (老年代剩余空間小于平均晉升大小 || 不允許擔(dān)保失敗 -> fullgc , else -> minorgc)
結(jié)論:
- HandlePromotionFailure設(shè)置成了false,每次minorgc時(shí)只要老年代容不下所有年輕代了,都會(huì)被改為fullgc??!
- 設(shè)置為true后,只有靠平均晉升大小來(lái)判斷是否觸發(fā)fullgc了,這樣的經(jīng)驗(yàn)判斷,肯定會(huì)有失誤的時(shí)候,一旦某次晉升對(duì)象大小大于經(jīng)驗(yàn)值,此時(shí)會(huì)再觸發(fā)一次fullgc
- 所以,一般都會(huì)將HandlePromotionFailure設(shè)置為true,不要讓虛擬機(jī)頻繁的fullgc