解決如何回收問題,首先須要解決回收對象的問題?什麼樣的對象須要回收,怎麼樣的不須要回收?
保證有引用的內存不被釋放;回收沒有指針引用的內存是Collector的職責,在保證沒有指針引用的內存對象中,通常有兩種廣泛的對象檢測策略:算法
一、引用計數算法安全
原理:給對象添加一個引用計數器,每當有一個地方引用它時,計數器加1;引用失效時,計數器減1;計數器爲0說明可被回收。服務器
缺點:若是對象之間相互循環引用A.instance=B,B.instance=A,若是A=null,B=null,那麼A和B的引用計數不變,因此引用計數法不可行spa
二、可達性分析法指針
原理:經過一些列稱爲「GC Roots」的對象做爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證實此對象是不可用的。對象
在Java中GC Roots的對象包括:虛擬機棧中引用的對象,方法區類靜態屬性引用的對象,方法區常量引用的對象,本地方法棧中JNI引用的對象blog
圖1:可達性分析獲得的引用鏈集合內存
一、強引用:在程序代碼之中存在的,相似於「Object obj=new Object()」,主要強引用存在,垃圾收集器就不會回收掉被引用的對象虛擬機
二、軟引用(溢出以前):用來描述一些還有用可是並不是必需的對象。在系統將要發生內存溢出以前,將這些對象列進回收範圍之中進行第二次回收,相似於緩衝機制使用io
三、弱引用(垃圾回收以前):被弱引用的對象只能生存在下一次垃圾收集發生以前
四、虛引用:虛引用對象沒法得到對象實例,惟一的目的是在這個對象被收集器回收時收到一個系統通知
一、標記-清除算法
圖2:標記-清除算法(先標記後清除)
缺點:效率不高,空間會產生不連續的碎片
二、複製算法
圖3:複製算法
優勢:內存分配時不用考慮內存的碎片問題,實現簡單高效
缺點:浪費空間
三、標誌清理算法
圖4:標誌-清理算法
優勢:改進了標誌清除算法和複製算法的缺點,利用時間來換取空間。
對象的內存分配,往大方向講,就是在堆上分配,對象主要分配的新生代的Eden區上,當Eden區沒有足夠的空間進行分配時,虛擬機將會發起Minor GC,MinorGC是指對新生代的垃圾回收動做,Minor GC很是頻繁,因此要保證回收速度,通常採用複製算法。相對於Minor GC,Full GC發生在老年代,是指對老年代的收集動做,一次FC,每每伴隨着一次Minor GC,由於FC速度通常會比MC慢10倍,因此通常採用標記清理算法。
大對象經過設置PretenureSizeThreshold參數,大於這個參數的直接進入老生代,由於大對象會致使Eden區域提早觸發MC。
長期存活的對象將進入老年代,虛擬機爲每個對象定義一個Age計數器,在Eden中通過一次MC後若是存活,而且可以被Survivor容納的話,就會被移到Survivor中,Age=1,在通過一次MC後,+1,當到達MaxTenuringThreshold的時候【默認15】,對象進入老年代【資歷變老】,若是在Survivor中相同年齡的全部對象大小的總和大於Survivor的一半時,這個時候年齡大於或者等於這個年齡的對象就能夠直接進入老年代,無需等待Age到達MaxTenuringThreshold。
在發生Minor GC以前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代全部對象總空間,若是這個條件成立,那麼Minor GC能夠確保是安全的。若是不成立,則虛擬機會查看HandlePromotionFailure設置值是否容許擔保失敗。若是容許,那麼會繼續檢查老年代最大可用的連續空間是否大於歷次晉升到老年代對象的平均大小,若是大於,將嘗試着進行一次Minor GC,儘管此次Minor GC是有風險的;若是小於,或者HandlePromotionFailure設置不容許冒險,那這時也要改成進行一次Full GC。
下面解釋一下「冒險」是冒了什麼風險,前面提到過,新生代使用複製收集算法,但爲了內存利用率,只使用其中一個Survivor空間來做爲輪換備份,所以當出現大量對象在Minor GC後仍然存活的狀況(最極端的狀況就是內存回收後新生代中全部對象都存活),就須要老年代進行分配擔保,把Survivor沒法容納的對象直接進入老年代。與生活中的貸款擔保相似,老年代要進行這樣的擔保,前提是老年代自己還有容納這些對象的剩餘空間,一共有多少對象會活下來在實際完成內存回收以前是沒法明確知道的,因此只好取以前每一次回收晉升到老年代對象容量的平均大小值做爲經驗值,與老年代的剩餘空間進行比較,決定是否進行Full GC來讓老年代騰出更多空間。
取平均值進行比較其實仍然是一種動態機率的手段,也就是說,若是某次Minor GC存活後的對象突增,遠遠高於平均值的話,依然會致使擔保失敗(Handle Promotion Failure)。若是出現了HandlePromotionFailure失敗,那就只好在失敗後從新發起一次Full GC。雖然擔保失敗時繞的圈子是最大的,但大部分狀況下都仍是會將HandlePromotionFailure開關打開,避免Full GC過於頻繁。
對於Xms,Xmx和Xmn參數的說明:
JVM初始分配的堆內存由-Xms指定,默認是物理內存的1/64;JVM最大分配的堆內存由-Xmx指定,默認是物理內存的1/4。默認空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制;
空餘堆內存大於70%時,JVM會減小堆直到-Xms的最小限制。所以服務器通常設置-Xms、-Xmx 相等以免在每次GC 後調整堆的大小。-Xmn:設置年輕代大小。年輕代有eden+from survivor+to survivor
整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小
本文參考了不少前輩整理的資料,在進行自我整理,若有問題,還望指正。