1. 程式人生 > >JVM(十一):記憶體分配

JVM(十一):記憶體分配

JVM(十一):記憶體分配

在前面的章節中,我們花了大量的篇幅去介紹 JVM 內的記憶體佈局、物件在記憶體中的狀態、垃圾回收的演算法和具體實現等。今天讓我們探討一下物件是如何分配記憶體的。

堆記憶體劃分

前面說過,從記憶體回收的角度來看,堆被分為以下幾個部分:

開始建立的物件大多都會直接分配到新生代一塊區域中,只有大物件和經過多次 GC 後依然存活的物件會放置在老年代中。

那麼一個物件在被創建出來後,何時存在與哪個記憶體區域中呢,其具體的分配策略又是什麼呢?下面就讓我們一起來看一下幾個具體的記憶體分配策略,瞭解一個物件在產生後該何去何從。

物件優先在Eden分配

首先,我們先複習一下 GC 的兩種分類:

Minor GC,又叫新生代GC:發生在新生代的 GC,由於大多數 Java 物件都是朝生夕死的,因此發生這種 GC 十分的頻繁,回收速度一般也會比較快。
Full/Major GC,又叫老年代GC:發生在老年代的 GC,一般會比 Minor GC 慢 10倍以上。

在大多數情況下,物件優先在 Eden 區進行分配。當 Eden 區空間不足時,將會發起一次 Minor GC。

大物件直接進入老年代

首先,我們來看一下什麼是大物件,其一般是指需要大量連續記憶體空間的 Java 物件。最典型的就是很長的字串和陣列。對虛擬機器來說大物件是很難處理的物件,因為虛擬機器需要一塊連續的大空間進行分配,不過更加難以處理的是朝生夕死的大物件,經常出現的大物件,導致頻繁地觸發 GC 來空出大量連續空間來安置它們,影響了效能。因此,這種情況也警醒開發人員在開發過程中要儘量避免這種問題。

長期存活物件進入老年代

因為 Java 的垃圾回收是採取的分代收集思想。因此新生代的物件也有一定的途徑進入老年代,對應的就是存活時間,為了定位每個物件的存活時間,JVM 給每個物件定義了一個物件年齡計數器,當物件在 Survivor 區每熬過一次 Minor GC,物件的年齡就 +1,當物件的年齡滿足一定的條件(預設為15),就將其晉升到老年代。

前面這種晉升老年代的條件顯得比較的死板。因此在 JVM 中還有一種靈活的方式:如果在 Survivor 空間中相同年齡的所有物件綜合大於 Survivor 空間的一半,那麼年齡大於等於該閥值的物件就可以直接進入老年代,而無須等到滿足要求的年齡。

空間分配擔保

上面說過,在新生代經過 Minor GC 存活的物件,滿足一定的條件會進入老年代 , 因此就需要保證老年代有充足的空間能夠分配 。這種擔保方式讓 JVM 在進行 Full GC 的時候,可以進行大膽的操作 (因為 Full GC 十分的耗時,影響效能, 因此在 Minor GC 前需要進行一定的判斷)。

上圖就是空間分配擔保的流程,此種方式可以避免 Full GC 過於頻繁,影響效能。

總結

在本文中,我們詳細講述了物件記憶體分配的一些通用策略。瞭解這些策略可以讓我們明白 JVM 分配物件的邏輯,寫出更加高效健壯的程式碼。

文章在公眾號「iceWang」第一手更新,有興趣的朋友可以關注公眾號,第一時間看到筆者分享的各項知識點,謝謝!筆芯!

本系列文章主要借鑑自《深入分析 JavaWeb 技術內幕》和《深入理解 Java 虛擬機器-JVM 高階特性與最佳實踐》。