1. 程式人生 > >jvm---4、記憶體分配及回收策略

jvm---4、記憶體分配及回收策略

記憶體分配:
物件的記憶體分配就是在堆上分配的(也可以經過JIT 編譯後被拆散為標量型並間接地棧上分配) ,物件主要分配在Eden 區上,如果啟動了本地執行緒分配緩衝,將按執行緒優先在TLAB 上分配,少數情況(如物件較大)會直接分配到老年代。具體的到看使用哪一種垃圾收集器組合和記憶體相關配置。


回收過程:
如果一個物件不可達,也不會立刻被回收,要先宣告一個物件死亡,至少要通過兩次標記:物件進行可達性分析後,發現一個物件不可達,那麼會被第一次標記並且進行一次篩選,篩選條件是此物件是否有必要執行finalize() 方法。沒有覆蓋finalizer() 方法或者 finalizer() 已經被呼叫過,虛擬機器將這兩種情況都視為“沒有必要執行”。如果一個物件被判定有必要執行finalize() 方法,那麼這個物件會被放入到 F-Queue 的佇列之中,並在稍後由一個虛擬機器建立的、並且低優先順序的Finalizer 執行緒去執行物件的finalizer() 方法,但是不保證等待執行結束,因為如果一個物件finalizer()方法執行很慢(比如死迴圈了),很可能F-Queue佇列中的其他物件永久在等待,導致整個記憶體回收系統崩潰。如果在finalizer() 方法中使用this 把自己賦值給某個變數或者成員變數讓這個物件變成可達的,那麼這個物件就會移出F-Queue 佇列。
注意:一個物件的finalizer() 方法只會被執行一次,如一個物件被回收是執行了finalizer() 方法又被設定為可達的了,那麼在下一次回收時就不執行finalizer() 方法了。


虛擬機器棧、本地方法棧、程式計數器 隨著方法結束或者執行緒的結束,記憶體就回收了。堆的回收就要看GC 組合了。


記憶體分配普遍規則:
1、物件在新生代Eden 中分配,當Eden 中沒有足夠的空間分配時,進行一次 Minor GC
2、大物件(大量連續記憶體空間的物件,如大陣列,大的字串)直接進入老年代。這個物件大小有個控制引數:PretenureSizeThreshold
3、長期存活的物件進入在老年代分配。JVM 給每一個物件定義了一個年齡計數器。如果物件在Eden建立並經過第一次GC後仍然存活,並且能被Survivor容納的話,就被複制到Survivor,並且物件年齡加1。對於Surivivor 中的物件每經過一個Minor GC 還存活,年齡就加1 ,當物件年齡到一定程度就會被複制到老年代。這個年齡有控制對數 MaxTenuringThreshold,預設是15。
這個物件年齡也不是絕對有用的,因為如果在Surivivor空間中的相同年齡所有物件大小的總和大於Surivivor 空間的一半,物件的年齡大於或者等於這個年齡就可以直接進入老年代,無須等到 MaxTenuringThreshold 設定的這個值。這叫做:動態物件年齡判定。
4、分配空間的擔保,  在Eden 分配記憶體時,如果記憶體不夠分配了,那麼需要進行一次Minor GC,Minor GC 時,可能新生代存活物件多Survivor 放不下,那麼就要把一些物件放到老年代了。這就是分配擔保。
過程:在發生Minor GC 之前,虛擬機器會先檢查老年代最大可用的連續空間是否大於新生代所有物件總空間。如果大於,那這次Minor GC 是安全的,可以進行Minor GC。如果小於,則檢視 HandlePromotionFailure 的值是否允許擔保失敗。如果不允許擔保失敗,則要進行一次 Full GC ; 如果允許擔保失敗,那麼繼續查老年代最大可用連續空間是否大於歷次晉升到老年代物件的平均大小,如果大於將會嘗試進行一次Minor GC(這次Minor 是有風險);如果小於則進行一次Full GC。

控制對數總結:
-XX:PrintGCDetails   收集器日誌引數,在JVM 發生垃圾收集時列印記憶體回收日誌,並有程序退出時輸出當前記憶體各區域分配情況
-XX:PretenureSizeThreshold=位元組   大於這個值的物件直接在老年代分配(可以避免大物件在新生代的Eden區和Survivor區之間複製)
-XX:MaxTenuringThreshold=15    物件年齡達到這個值時,被複制到老年代中
-XX:+HandlePromotionFailure    允許擔保失敗  -號就是不允許。 JDK6 Update 24 之後這個引數就沒有用了,規則變為:只要老年代的連續空間大於新生代物件總大小或者大於歷次晉升的平均大小就會進行Minor GC,否則進行Full GC


注:
Minor GC  新生代GC, 發生在新生代的垃圾收集動作,java 物件大多數都是在新生代並且存活時間短,所以Minor GC 非常頻繁並且速度比較快

Major GC /Full GC  老年代GC ,指發生在老年代的GC,出現了Major GC 經常會伴隨一次Minor GC (並非絕對),Major GC 比較 Minor GC 慢10倍以上

參考《深入理解JVM》