Java 的記憶體管理是每位開發者必須掌握的核心技能,它直接關係到應用程式的效能、穩定性與擴展性。本文將從記憶體分配與釋放、記憶體洩漏成因、JVM 記憶體區域劃分、資料儲存模型、垃圾回收機制以及最佳實踐等六大維度,系統梳理 Java 記憶體管理的完整知識體系。
Java 記憶體管理的本質在於解決「物件分配」與「物件釋放」兩大核心問題。分配端由程式設計師透過 new 關鍵字在堆中為物件申請空間(基本型別除外);釋放端則完全交給垃圾回收器自動處理。這種設計雖大幅降低了開發者的心智負擔,但也將監控每個物件的申請、引用、賦值等生命週期狀態的重任轉嫁給了 JVM,增加了執行時的開銷。
Java 中的記憶體洩漏指的是:某些已分配的物件同時滿足「可達性」(GC Roots 可達)與「無用性」(程式不再使用)兩個條件。這類物件雖仍被引用,但實際上已無業務價值,卻無法被 GC 回收,長期佔用寶貴的堆空間,最終導致 OutOfMemoryError。常見場景包括:靜態集合持有過期物件、監聽器未移除、緩存無淘汰策略等。
棧存放基本型別變數與物件引用變數,具有「先進後出」特性。優勢在於存取速度極快(僅次於 CPU 寄存器),且資料可共享(如字面值常量池)。劣勢是資料大小與生命週期必須編譯期確定,缺乏靈活性。
堆用於存放 new 出來的物件、陣列與實例變數。優勢在於動態分配、大小不固定、生命週期靈活;劣勢是存取速度較棧慢,且需 GC 介入管理。
8 種基本型別的字面值因大小固定、作用域明確,直接存放於棧中。編譯器會優化相同字面值的儲存:int a=3; int b=3; 會讓 a、b 指向棧中同一個「3」的地址。修改 a 為 4 時,會重新尋址或開闢新空間,不影響 b。
Rectangle rect = new Rectangle(3,5); 分為聲明(棧分配引用變數 rect,初始為 null)與實例化(堆分配實例變數、執行構造器、返回堆地址給引用)兩步。
每次 new 都在堆開闢獨立空間;若 r2 = r1,則棧中兩引用指向同一堆物件,修改其中一者狀態會影響另一者。
包裝類(如 Integer)本質是物件,存堆;String 兼具字面值與物件雙重特性:String s = "abc" 走常量池共享邏輯,new String("abc") 強制在堆創建新物件。
陣列引用在棧,實體在堆;static 變數屬於類級別,存於方法區,全局共享、類加載時初始化、生命週期隨類消亡。
透過一個包含 Collar、String name 的 Dog 類,展示 main 方法、go()、setName() 在棧幀中的進棧出棧過程,以及實例變數、局部變數、物件在堆棧間的具體分佈與生命週期關係。
GC 採用可達性分析算法,當物件無任何活線程可達時判定為垃圾。觸發條件通常為 CPU 空閒或堆空間不足,開發者無法精確控制執行時機,System.gc() 僅是建議而非強制。
sb = null;sb1 = sb2; 使原物件斷鏈物件被回收前會調用 finalize(),但執行時間不確定、甚至可能不執行,且會增加 GC 負擔,強烈建議不覆寫此方法,改用 try-with-resources 或 Cleaner 機制管理資源釋放。
final 修飾引用型變數時,僅保證「引用地址不變」,不保證「物件狀態不變」。final StringBuffer sb = new StringBuffer(); sb.append("x"); 編譯通過、運行正常。多線程下錯誤地以 final 當作不可變保證,極易引發併發 Bug。此外,final 實例變數必須在構造器結束前顯式賦值,類變數則無默認值。
s = "val" 利用常量池複用,避免重複創建物件。-Xms -Xmx 避免 OOM。Java 記憶體管理是一套精密的自動化體系,理解其底層運作原理——從堆棧分工、物件佈局、GC 算法到 JVM 參數調優——能幫助開發者寫出高效、穩定、可擴展的系統。記住:好的架構師不僅懂業務,更懂記憶體。