JVM GC回收哪個區域內的垃圾?
JVM GC只回收堆區和方法區內的對象
虛擬機棧、程序計數器、本地方法棧為線程私有,不需要回收
JVM GC什么時候執行?
伊甸區滿的時候,執行Young GC
老年區滿的時候,執行Full GC
如何判斷一個對象是否可以被回收?
1、引用計數法(有缺陷,無法解決循環引用問題,JVM 沒有采用)
2、可達性分析(解決了引用計數的缺陷,被 JVM 采用)
什么是引用計數法?
對象被引用一次,計數器+1,失去引用,計數器-1,當計數器在一段時間內為0時,即認為該對象可以被回收了。
(無法解決相互引用的問題,例如:A引用B,B引用A,它們永遠都不會再被使用)
什么是可達性分析?
通過一系列的稱為 “GC Roots” 的對象作為起始點,從這些節點開始向下搜索,所有所走過的路徑稱為引用鏈,當一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可用的
Java中可以作為GC ROOT的對象有哪些?
1、虛擬機棧中引用的對象(本地變量表)
2、方法區中靜態屬性引用的對象
3、方法區中常量引用的對象
4、本地方法棧中引用的對象(Native對象)
JVM中將對象的引用分為了四種類型
1、強引用:new出來的對象都是強引用,GC無論如何都不會回收,即使拋出OOM異常。
2、軟引用:只有當JVM內存不足時才會被回收。
3、弱引用:只要GC,就會立馬回收,不管內存是否充足。
4、虛引用:可以忽略不計
什么是分代回收?
對於新生代內存的回收使用Young GC主要采用復制算法。
而對於老年代的回收使用Full GC,大多采用標記-整理算法。
為什么需要分帶回收?
JVM為了優化對內存的回收
常見的GC算法
1、復制(停止-復制 效率低,需要的空間大,優點,不會產生碎片)
2、標記-清除(標記 - 清除算法 速度較快,占用空間少,標記清除后會產生大量的碎片)
3、標記-壓縮(在標記-清除的基礎上移動數據,避免產生內存碎片)
復制算法
復制算法采用的方式為從根集合進行掃描,將存活的對象移動到一塊空閑的區域
當存活的對象較少時,復制算法會比較高效(新生代的Eden區就是采用這種算法)
標記-清除
該算法采用的方式是從根集合開始掃描,對存活的對象進行標記,標記完畢后,再掃描整個空間中未被標記的對象,並進行清除。標記-清除動作不需要移動對象,且僅對不存活的對象進行清理,在空間中存活對象較多的時候,效率較高,但由於只是清除,沒有重新整理,因此會造成內存碎片。
標記-壓縮
該算法與標記-清除算法類似,都是先對存活的對象進行標記,然后清除未被標記的對象,但是在清除后會把活的對象向左端空閑空間移動,然后再更新其引用對象的指針,該算法避免了標記-清除的碎片問題,但由於需要進行移動,因此成本也增加了。(該算法適用於老年代)
垃圾回收器簡介
每一個回收器都存在Stop The World 的問題,只不過各個回收器在Stop The World 時間優化程度、算法的不同
常見的垃圾收集器
1、Serial (新生代)
2、ParNew (新生代)
3、ParallelScavenge(新生代)
4、SerialOld (老年代)
5、ParallelOld(老年代)
6、CMS (老年代)
7、G1 (新生代和老年代)
CMS的一大特點,就是用兩次短暫的暫停來代替串行或並行標記整理算法時候的長暫停。
CMS的缺點
1、內存碎片
2、需要更多的CPU資源
3、需要更大的堆空間
G1既可以回收新生代,又可以回收老年代