通常GC採用有向圖的方式記錄和管理堆區中的所有對象 JVM將堆記憶體劃分為 Eden、Survivor 和 Tenured/Old 空間。 1. 年輕代 所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命周期短的對象,對應的是Minor GC,每次 Minor GC ...
通常GC採用有向圖的方式記錄和管理堆區中的所有對象
JVM將堆記憶體劃分為 Eden、Survivor 和 Tenured/Old 空間。
1. 年輕代
所有新生成的對象首先都是放在Eden區。 年輕代的目標就是儘可能快速的收集掉那些生命周期短的對象,對應的是Minor GC,每次 Minor GC 會清理年輕代的記憶體,演算法採用效率較高的複製演算法,頻繁的操作,但是會浪費記憶體空間。當“年輕代”區域存放滿對象後,就將對象存放到年老代區域。
2. 年老代
在年輕代中經歷了N(預設15)次垃圾回收後仍然存活的對象,就會被放到年老代中。因此,可以認為年老代中存放的都是一些生命周期較長的對象。年老代對象越來越多,我們就需要啟動Major GC和Full GC(全量回收),來一次大掃除,全面清理年輕代區域和年老代區域。
3. 持久代
用於存放靜態文件,如Java類、方法等。持久代對垃圾回收沒有顯著影響。
Minor GC:
用於清理年輕代區域。Eden區滿了就會觸發一次Minor GC。清理無用對象,將有用對象複製到“Survivor1”、“Survivor2”區中(這兩個區,大小空間也相同,同一時刻Survivor1和Survivor2只有一個在用,一個為空)
·Major GC:
用於清理老年代區域。
·Full GC:
用於清理年輕代、年老代區域。 成本較高,會對系統性能產生影響。
記憶體回收機制:
記憶體回收就是釋放掉在記憶體中已經沒用的對象。
首先,要判斷怎樣的對象是沒用的對象。這裡有2種方法:
1.採用標記計數的方法(引用計數法):
給記憶體中的對象給打上標記,對象被引用一次,計數就加1,引用被釋放了,計數就減一,當這個計數為0的時候,這個對象就可以被回收了。當然,這也就引發了一個問題:迴圈引用的對象是無法被識別出來並且被回收的。所以就有了第二種方法:
2.採用根搜索演算法(引用可達法):
從一個根出發,搜索所有的可達對象,這樣剩下的那些對象就是需要被回收的
判斷完了哪些對象是沒用的,這樣就可以進行回收了
最簡單的,就是直接清空那個需要被回收的對象。但是這又出現了一個問題,就是記憶體會被分為一塊一塊的小碎片。
為瞭解決這個問題,可以採用第二種方法,就是在之前的基礎上將存活的對象給整理一下,使他們變成一個連續的記憶體,從而釋放出連續的較大的記憶體空間。
還有一中回收方法就是採用複製的辦法:將記憶體分為2塊,一塊用來存放對象,另一塊用來放著,當存放對象的那塊滿了以後就將上面存活的對象給複製過來,然後在這塊記憶體上工作,並且將之前的記憶體清空,當自己這塊滿了以後再複製回去,如此反覆。
垃圾回收過程:
1、新創建的對象,絕大多數都會存儲在Eden中,
2、當Eden滿了(達到一定比例)不能創建新對象,則觸發垃圾回收(GC),將無用對象清理掉,
然後剩餘對象複製到某個Survivor中,如S1,同時清空Eden區
3、當Eden區再次滿了,會將S1中的不能清空的對象存到另外一個Survivor中,如S2,
同時將Eden區中的不能清空的對象,也複製到S1中,保證Eden和S1,均被清空。
4、重覆多次(預設15次)Survivor中沒有被清理的對象,則會複製到老年代Old(Tenured)區中,
5、當Old區滿了,則會觸發一個一次完整地垃圾回收(FullGC),之前新生代的垃圾回收稱為(minorGC)
finalized()方法一個對象只能執行一次,只能在第一次進入被回收的隊列,而且對象所屬於的類重寫了finalize方法才會被執行。第二次進入回收隊列的時候,不會再執行其finalize方法,而是直接被二次標記,在下一次GC的時候被GC。 放一張圖吧 垃圾回收他是在虛擬機空閑的時候或者記憶體緊張的時候執行的,什麼時候回收不是由程式員來控制的,這也就是java比較耗記憶體的原因之一。還有在垃圾回收的時候當檢測到對象沒有用了,需要被回收的時候並不會馬上被回收,而是將其放入到一個準備回收的隊列,去執行finalize方法。。然等到下次記憶體回收的時候要是他還是沒有被任何人引用的話,就將其給回收了。 面試問題
- 怎樣確定哪些對象是不再被引用的對象?(兩次標記)
- 什麼時候回收?如何回收?
- 垃圾收集器