簡述JAVA GC回收機制,深入理解GC原理
什麼是“GC”
垃圾回收機制。
為什麼要用“GC”
眾所周知,JAVA 這語言,與C語言不同,Java記憶體(堆記憶體)的分配與回收由JVM垃圾收集器自動完成,比如 C語言自己定義的變數,不用時需要 自己回收這個變數 。JAVA 這就是自動完成了,自動檢測,無用的垃圾,回收,從而釋放記憶體。一個人工,一個自動化。
先看一下JVM記憶體結構
棧:存放區域性變數
堆:存放所有new出來的東西
方法區:被虛擬機器載入的類資訊、常量、靜態常量等。
程式計數器(和系統相關)
本地方法棧
堆記憶體就是GC管理的主要區域(知識點,重點)
然後JVM又把堆記憶體分三代,新生代,老年代,持久代。如下圖
-
新生代:分為eden ,From Survivor ,TO Survivor
絕大多數剛建立的物件會被分配在Eden區,其中的大多數物件很快就會消亡。Eden區是連續的記憶體空間,因此在其上分配記憶體極快。(每次新生代的垃圾回收(又稱Minor GC)採用copy演算法,之後再講)
最初一次,當Eden區滿的時候,執行Minor GC,將消亡的物件清理掉,並將剩餘的物件複製到一個存活區From (此時,TO 是空白的,兩個Survivor總有一個是空白的);
下次Eden區滿了,再執行一次Minor GC,將消亡的物件清理掉,將存活的物件複製到TO 中,然後清空Eden
如果沒有填滿,當兩個存活區切換了幾次(每進行一次MinorGC,都會在存活的物件做一個標記,加1,當標記的值大於15,HotSpot虛擬機器預設15次,用-XX:MaxTenuringThreshold
-
老年代
在新生代中經歷了N次垃圾回收後仍然存活的物件,就會被放到年老代,該區域中物件存活率高。老年代的垃圾回收(又稱Major GC)通常使用“標記-清理”或“標記-整理”演算法。整堆包括新生代和老年代的垃圾回收稱為Full GC(HotSpot VM裡,除了CMS之外,其它能收集老年代的GC都會同時收集整個GC堆,包括新生代)
當老年代的空間不足時,會觸發Major GC/Full GC,速度一般比Minor GC慢10倍以上。
-
持久代(永久代)
在JDK8之前的HotSpot實現中,類的元資料如方法資料、方法資訊(位元組碼,棧和變數大小)、執行時常量池、已確定的符號引用和虛方法表等被儲存在永久代中,32位預設永久代的大小為64M,64位預設為85M,可以通過引數-XX:MaxPermSize進行設定。GC不會在主程式執行期對永久區域進行清理,這也導致了永久代的區域會隨著載入的Class的增多而脹滿,最終丟擲OOM異常。
所以虛擬機器團隊在JDK8的HotSpot中,把永久代從Java堆中移除了,並把類的元資料直接儲存在本地記憶體區域(堆外記憶體),稱之為元空間。
元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制。類的元資料放入 native memory, 字串池和類的靜態變數放入java堆中. 這樣可以載入多少類的元資料就不再由MaxPermSize控制, 而由系統的實際可用空間來控制。
總結:
為了分代垃圾回收,Java堆記憶體分為3代:新生代,老年代和永久代。
新的物件例項會優先分配在新生代,在經歷幾次Minor GC後(預設15次),還存活的會被移至老年代(某些大物件會直接在老年代分配)。
Minor GC發生在新生代,當Eden區沒有足夠空間時,會發起一次Minor GC,將Eden區中的存活物件移至Survivor區。Major GC發生在老年代,當升到老年代的物件大於老年代剩餘空間時會發生Major GC。
發生Major GC時使用者執行緒會暫停,會降低系統性能和吞吐量。
JVM的引數-Xmx和-Xms用來設定Java堆記憶體的初始大小和最大值。依據個人經驗這個值的比例最好是1:1或者1:1.5。比如,你可以將-Xmx和-Xms都設為1GB,或者-Xmx和-Xms設為1.2GB和1.8GB。
Java中不能手動觸發GC,但可以用不同的引用類來輔助垃圾回收器工作