JVM-垃圾回收

帶着三個問題看垃圾回收 
1.回收誰
2.什麼時候回收
3.怎麼回收

1.回收誰

引用計數法:給對象中添加一個引用計數器,每當有對象引用它時,計數器就加1,引用失效就減1,計數器到0的時候代表不能使用該對象,不能解決循環引用的問題

可達性分析:通過GCRoots做爲起點,從這個起點向下搜索,當一個對象到GCRoots沒有任何引用鏈相連的時候,這個對象是不可用的,可以回收。虛擬機棧(棧幀變量表中引用的對象),方法區(類的靜態屬性引用的對象,常量引用的對象),本地方法中JNI(Native引用的對象)
image.png

"食之無味,棄之可惜"
強引用(認可OOM,也不會回收)
軟引用(系統OOM之前,這些對象被回收)
弱引用(無論內存夠不夠,都會回收)
虛引用(只會收到回收通知)

"最後一刻掙扎"
一個對象的死亡,至少要被標記兩次,第一次看有沒有必要執行該對象中的finalize方法,如果該方法被調用過或者對象沒有覆蓋整個方法,就沒有必要執行finalize。如果執行了finalize,可以在方法裏面自救,自救方案是與引用鏈上任何一個對象關聯即可。不建議使用

方法區的回收
回收效率低,回收嚴謹。只有滿足以下三點纔會回收
1.該類的所有實例都被回收,堆中不存在任何該類的實例
2.加載該類的ClassLoader已被回收
3.該類的java.lang.Class對象沒有任何地方被應用,無法通過反射來訪問該類

2.什麼時候回收

應用線程空閒時
內存滿的時候

3.怎麼回收

標記清除算法
image.png
先標記要回收的對象,在統一清除。缺陷是會產生大量不連續的內存碎片,在分配大對象時,不得不提前觸發另一次垃圾收集動作

複製算法
image.png
將內存分AB兩塊,每次只用一塊,A的內存用完了,回收的時候就將A還存活的對象放在B上,然後統一清理A。缺陷是對象多的時候浪費複製時間,對內存的開銷也比較大。

標記整理算法
image.png
標記出所有要回收的對象,標記完成後,將存活的對象移動一端,然後清理掉端邊界以外的內存。

分代收集算法
根據新生代和老年代的特點,使用分代收集算法
image.png
因爲新生代朝生夕死,所以用複製算法,僅需要複製少量對象。
老年代存活率高,對象多,沒有額外空間進行分配,就使用標記-整理算法。可以自由搭配很多種,不過大致的類型就是以下幾種。

串行收集器(Serial)
image.png
-XX:+SerialGC
單線程收集器,這裏的單線程不是指垃圾回收的線程只有一個,而是相對於應用程序來講,在回收垃圾的時候要暫停應用程序(STW)
在內存不足時,串行GC設置停頓標識,當所有線程到達安全點後,應用程序暫停,開始垃圾收集。適合堆內存不高且單核的cpu使用。

並行收集器(ParNew,Parallel Scavenge)
image.png
-XX:+UseParNewGC
ParNew是Serial收集器的多線程版本,搭配老年代CMS首選。適合多核cpu。ParallelScavenge更關注吞吐量,同樣需要STW,適合和前臺交互少的系統,後臺處理任務量大的

併發收集器(CMS)
更關注低延遲的收集器,分爲以下四個階段,適合內存大,多核cpu。缺陷:消耗內存過的大,容易引起fullGC。有碎片,爲防止fullGC,默認開啓碎片整理參數
image.png

初始標記:以STW的方式標記所有根對象,很快
併發標記,與程序併發執行,標記出所有根路徑的可達路徑
重新標記,以STW標記有可能在這期間錯過的,同樣很快
併發清除,將不可達對象併發回收

G1收集器
引入了 Region概念,和CMS比較像,只不過有Region的優勢
image.png

觀看GC日誌
image.png
33.125代表虛擬機啓動到現在,經過了多少秒
Full GC和GC代表停頓類型,不是爲了區分新生代和老年代的,如果有Full,代表是以SWT觸發的垃圾收集
DefNew,Tenured,Perm纔是區域,發生在什麼區域上的
3324K - > 152K(3712K) :GC前該內存區域已使用的容量 -> GC後該內存已使用的容量(總容量)

內存分配與回收策略

  • 對象優先在Eden分配,如果說Eden內存空間不足,就會發生Minor GC
  • 大對象直接進入老年代,大對象需要大量連續內存空間的Java對象,比如很長的字符串和大型數組,1、導致內存有空間,還是需要提前進行垃圾回收獲取連續空間來放他們,2、會進行大量的內存複製。-XX:PretenureSizeThreshold 參數 ,大於這個數量直接在老年代分配,缺省爲0 ,表示絕不會直接分配在老年代。
  • 長期存活的對象將進入老年代,默認15歲,-XX:MaxTenuringThreshold調整
  • 動態對象年齡判定,爲了能更好地適應不同程序的內存狀況,虛擬機並不是永遠地要求對象的年齡必須達到了MaxTenuringThreshold才能晉升老年代,如果在Survivor空間中相同年齡所有對象大小的總和大於Survivor空間的一半,年齡大於或等於該年齡的對象就可以直接進入老年代,無須等到MaxTenuringThreshold中要求的年齡
  • 空間分配擔保:新生代中有大量的對象存活,survivor空間不夠,當出現大量對象在MinorGC後仍然存活的情況(最極端的情況就是內存回收後新生代中所有對象都存活),就需要老年代進行分配擔保,把Survivor無法容納的對象直接進入老年代.只要老年代的連續空間大於新生代對象的總大小或者歷次晉升的平均大小,就進行Minor GC,否則FullGC。

內存泄漏和內存溢出
內存泄漏是該釋放的對象沒有得到釋放
內存溢出是撐爆了內存,對象太多了

JDK爲我們提供的工具

  • jps

列出當前機器上正在運行的虛擬機進程
-p :僅僅顯示VM 標示,不顯示jar,class, main參數等信息.
-m:輸出主函數傳入的參數. 下的hello 就是在執行程序時從命令行輸入的參數
-l: 輸出應用程序主類完整package名稱或jar完整名稱.
-v: 列出jvm參數, -Xms20m -Xmx50m是啓動程序指定的jvm參數

  • jstat

是用於監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據,在沒有GUI圖形界面,只提供了純文本控制檯環境的服務器上,它將是運行期定位虛擬機性能問題的首選工具。
假設需要每250毫秒查詢一次進程2764垃圾收集狀況,一共查詢20次,那命令應當是:jstat-gc 2764 250 20
常用參數:
-class (類加載器)
-compiler (JIT)
-gc (GC堆狀態)
-gccapacity (各區大小)
-gccause (最近一次GC統計和原因)
-gcnew (新區統計)
-gcnewcapacity (新區大小)
-gcold (老區統計)
-gcoldcapacity (老區大小)
-gcpermcapacity (永久區大小)
-gcutil (GC統計彙總)
-printcompilation (HotSpot編譯統計)

  • jmap
  • jstack
  • .........
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章