記憶體管理是電腦軟體開發的重要組成部分,負責應用程式中記憶體的有效分配、利用和釋放。其重要性在於增強軟體效能,確保系統穩定性。
垃圾收集(GC)在 Java 和 Go 等當代程式語言中至關重要。它會自動檢測並回收未使用的內存,從而減輕開發人員手動管理內存的需要。 GC 的概念最初出現在 20 世紀 50 年代末的 LISP 程式語言中,標誌著自動記憶體管理的引入。
自動化記憶體管理的主要優點包括:
了解記憶體中「垃圾」的本質並識別可回收空間至關重要。在接下來的章節中,我們將從探索垃圾收集的基本原理開始。
引用計數演算法在物件標頭中分配一個欄位來追蹤其引用計數。此計數隨著每個新引用而增加,並在刪除引用時減少。當計數達到零時,該物件就有資格進行垃圾回收。
考慮以下程式碼:
先建立一個有值demo的字串,由d引用(圖1)。
String d = new String("demo");
圖 1 – 建立字串後
然後,將 d 設定為 null。 demo的引用計數為零。在引用計數演算法中,需要回收demo的記憶體(圖2)。
d =null; // Reference count of 'demo' becomes zero, prompting garbage collection.
圖 2 – 當引用無效時
引用計數演算法在程式執行期間運行,避免 Stop-The-World 事件,這些事件會暫時停止程式以進行垃圾回收。然而,它的主要缺點是無法處理循環引用(圖 3)。
例如:
public class CircularReferenceDemo { public CircularReferenceDemo reference; private String name; public CircularReferenceDemo(String name) { this.name = name; } public void setReference(CircularReferenceDemo ref) { this.reference = ref; } public static void main(String[] args) { CircularReferenceDemo objA = new CircularReferenceDemo("Ref_A"); CircularReferenceDemo objB = new CircularReferenceDemo("Ref_B"); objA.setReference(objB); objB.setReference(objA); objA = null; objB = null; } }
這裡,儘管使外部引用無效,但 objA 和 objB 之間的相互引用阻止了它們的垃圾回收。
圖 3 – 循環引用
我們可以看到這兩個物件都無法再存取。然而,它們是互相引用的,因此它們的引用計數永遠不會為零。因此,GC 收集器永遠不會被通知使用引用計數演算法對它們進行垃圾收集。
該演算法實際上是透過使用 std::shared_ptr 在 C++ 中實現的。 std::shared_ptr 旨在管理動態分配物件的生命週期,在建立或銷毀指向物件的指標時自動增加和減少參考計數。此智慧型指標是 C++ 標準函式庫的一部分,提供強大的記憶體管理功能,可大幅降低與手動記憶體處理相關的風險。每當複製 std::shared_ptr 時,託管物件的內部參考計數就會增加,反映新的參考。相反,當 std::shared_ptr 被破壞、超出範圍或重新分配給不同的物件時,引用計數就會減少。當引用計數為零時,分配的記憶體會自動回收,並且物件會被銷毀,
透過確保沒有必要時不會保留分配的對象,有效防止記憶體洩漏。
可達性分析演算法從GC根開始,遍歷物件圖。無法從這些根到達的物件被視為不可恢復,並成為收集的目標。
如下圖所示,藍色圓圈內的物件應該保持存活,灰色圓圈內的物件可以回收(圖4)。
圖 4 – 記憶體洩漏
此方法有效解決了引用計數演算法固有的循環引用問題。從 GC 根無法到達的物件將被分類進行收集。
通常,被視為 GC 根的 Java 物件包括:
GraalVM 提供了一個提前 (AOT) 編譯器,它將 Java 應用程式轉換為獨立的可執行二進位文件,稱為 GraalVM Native Images。這些二進位檔案由 Oracle 實驗室開發
封裝應用程式和函式庫類別以及 GC 等執行時間元件,允許在沒有 Java 執行時間環境 (JRE) 的情況下進行操作。
這個過程涉及靜態分析以確定可存取的元件、透過執行區塊進行初始化,以及透過建立應用程式狀態快照以供後續機器碼翻譯來完成。
Substrate VM 是 GraalVM 套件的一個組成部分,由 Oracle 實驗室精心策劃。它是一個增強的 JVM,不僅支援提前 (AOT) 編譯,還有助於執行 Java 以外的語言,例如 JavaScript、Python、Ruby,甚至是 C 和 C++ 等本機語言。 Substrate VM 的核心是一個複雜的框架,允許 GraalVM 將 Java 應用程式編譯成獨立的本機二進位。這些二進位檔案不依賴傳統的 Java 虛擬機器 (JVM) 來執行,這簡化了部署並
操作流程。
Substrate VM 的主要功能之一是其專門的垃圾收集器,它針對需要低延遲和最小記憶體佔用的應用程式進行了微調。該垃圾收集器擅長處理與本機映像不同的獨特記憶體佈局和操作模型,這與在標準 JVM 上運行的傳統 Java 應用程式有很大不同。 Substrate VM 本機映像中缺少即時 (JIT) 編譯器是一種策略選擇,有助於最小化執行檔的整體大小。這是因為它消除了包含 JIT 編譯器和相關元資料的必要性,這些元資料的大小和複雜性都很大。
此外,雖然 GraalVM 是使用 Java 開發的,但這會帶來一定的限制,特別是在本機記憶體存取權方面。此類限制主要是出於安全考慮以及保持跨平台相容性的需要。然而,存取本機記憶體對於優化垃圾收集操作至關重要。為了解決這個問題,Substrate VM 採用了一套專門的介面來促進與本機記憶體的安全且有效率的互動。這些介面是更廣泛的 GraalVM 架構的一部分,使 Substrate VM 能夠以類似於 C 等較低階語言的方式有效管理內存,同時保留 Java 的安全性和可管理性。
在實務中,這些功能使 Substrate VM 成為一種極其通用的工具,可以增強使用 GraalVM 編譯的應用程式的功能和效率。透過允許開發人員
Substrate VM 利用更廣泛的程式語言並將其編譯為高效的本機二進位文件,突破了傳統 Java 開發環境所能實現的界限。這使其成為需要高效能、減少資源消耗和多種語言支援的現代軟體開發專案的寶貴資產。
Substrate VM 值得注意的元素包括:
透過用於原始記憶體操作的指標介面 Pointer 和用於處理字大小值的 WordBase 介面 WordBase 等介面簡化記憶體存取。
將堆劃分為包含不可變物件的預初始化段和用於動態物件分配的運行時段(圖 5)。
圖 5 – 本機映像中的記憶體管理
在執行時,Substrate VM 中所謂的鏡像堆包含在鏡像建置過程中所建立的物件。堆的這一部分是用可執行二進位檔案的資料部分中的資料預先初始化的,並且可以在應用程式啟動時輕鬆存取。駐留在圖像堆中的物件被認為是不朽的;因此,這些物件中的引用被
視為根指針
垃圾收集器。但是,GC 僅掃描部分映像堆中的根指針,特別是那些未標記為唯讀的指針。
在建置過程中,指定為唯讀的物件被放置在影像堆的特定唯讀部分。由於這些物件永遠不會保存對運行時分配的物件的引用,因此它們不包含根指針,從而允許 GC 在掃描期間繞過它們。同樣,僅由原始資料或原始類型數組組成的物件也缺少根指標。此屬性進一步簡化了垃圾收集過程,因為這些物件可以從 GC 掃描中省略。
相較之下,Java 堆被指定用於保存在執行時間動態建立的普通物件。堆的這一部分會定期進行垃圾收集,以回收不再使用的物件所佔用的空間。它被建構為具有老化機制的分代堆,隨著時間的推移促進高效的記憶體管理。
預先初始化、永久映像堆和動態管理的 Java 堆之間的這種劃分使 Substrate VM 能夠優化記憶體使用和垃圾收集效率,滿足應用程式記憶體需求的靜態和動態方面。
在 Substrate VM 的堆模型中,記憶體被系統地組織成稱為堆塊的結構。這些區塊的預設大小通常為 1024KB,形成一個連續的虛擬記憶體段,僅分配給物件儲存。這些塊的組織結構是一個鍊錶,其中尾部塊代表最近添加的段。這樣的模特兒
促進高效的記憶體分配和對像管理。
這些堆塊進一步分為兩種:對齊和未對齊。對齊的堆塊能夠連續保存多個物件。這種對齊方式可以更簡單地映射
物件到各自的父堆塊,使記憶體管理更加直覺和有效率。在需要物件提升的場景中 - 通常是在垃圾收集期間和
記憶體最佳化-物件從其在父堆區塊中的原始位置移動到位於指定的「舊目標空間」中的目標堆塊。此遷移是分代堆管理策略的一部分,該策略透過將年輕物件與舊物件分離來幫助優化垃圾收集過程,從而減少 GC 週期期間的開銷。
GraalVM Native Image 支援針對不同需求客製化的各種 GC:
序列GC:預設的低佔用空間收集器,適用於單執行緒應用程式。
G1 垃圾收集器: 專為具有大堆大小的多執行緒應用程式而設計,增強了產生管理的靈活性。
Epsilon GC: 一種簡約的收集器,處理分配但缺乏回收,最適用於可預測堆完全利用率的短期應用。
總之,Substrate VM 透過結合專門的垃圾收集和結構化堆管理等先進技術,有效地優化了 GraalVM 中的記憶體管理。這些功能(包括堆塊以及用於圖像和 Java 堆的單獨記憶體段)可簡化垃圾收集並提高應用程式效能。由於 Substrate VM 支援多種程式語言並將其編譯為高效的本機二進位文件,因此它展示了現代 JVM 框架如何超越傳統邊界,以提高不同應用程式環境中的執行效率和穩健性。這種方法為虛擬機器技術和應用程式部署的未來發展設定了高標準。
以上是GraalVM 本機映像中的記憶體管理的詳細內容。更多資訊請關注PHP中文網其他相關文章!