Java記憶體回收
Java記憶體區域
方法區、元空間
各個執行緒共享的記憶體區域,用於儲蓄已被虛擬機器器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。 方法區有時也被稱為永久代。在 JDK1.8 及之後取消了永久代,取而代之的是元空間。在永久代中做調優是十分困難的,且效果不明顯。永久代的空間大小受制於 JVM 本身記憶體限制,而元空間是直接使用機器的記憶體,只受系統記憶體限制。元空間預設最大大小為無限制。 一些引數:
-XX:PermSize=N //方法區初始大小
-XX:MaxPermSize=N //方法區的最大大小
-XX:MetaspaceSize=N //元空間的初始大小
-XX:MaxMetaspaceSize=N //原空間的最大大小
複製程式碼
堆
堆是 Java 虛擬機器器所管理的記憶體中最大的一塊,是所有執行緒所共享的,此記憶體區域的唯一目的就是存放物件例項。堆是垃圾收集器管理的主要區域。在 HotSpot 中,大多數情況下記憶體被分為新生代和老年代,預設分配比例為 1:2。在新生代中又被分為一個 Eden 和兩個 Survivor ,分配比例為 8:1:1。新生代中物件的年齡在經歷一次 Minor GC 後年齡會+1,當年齡達到15(預設值)後會進入老年代。 一些引數:
-XX:InitialHeapSize=N //堆初始大小
-Xms1024m //簡寫值
-XX:MaxHeapSize=N //堆的最大值
-Xmx1024m //簡寫值
-XX:MaxTenuringThreshold=15 //新生代進入老年代的年齡
複製程式碼
棧區
執行緒私有,用於儲存區域性變量表、運算元棧、動態連結、方法出口等資訊。包含程式計數器、虛擬機器器棧和本地方法棧。
垃圾收集演演算法
哪些物件需要回收?
- 引用計數演演算法 給物件一個引用計數器,每當有一個地方引用它時,計數器就加 1,當引用失效時就減 1。任何計數器為 0 的物件都是不被使用的物件。 缺點:難以解決迴圈引用問題。
- 可達性分析演演算法 通過一系列的“GC Roots”作為起點,從這些節點開始向下搜尋,搜尋所達到的路徑稱為引用鏈。當一個物件不在任何引用鏈中,則此物件是不被使用的物件。
什麼時候回收? 在 可達性分析演演算法 中從 GC Roots 搜尋時,必須保證引用的一致性,以使物件的引用關係不再發生變化。這點就導致了 GC 必須停止所有的執行執行緒(Stop The World)。 在 HotSpot 中使用 OopMap 來記錄呼叫資訊。在程式碼中有 OopMap 記錄的地方稱為 SafePoint。當 GC 發生時,需要讓所有執行緒先跑到 SafePoint 再執行 GC 操作。
如何回收?
- 標記-清除演演算法
同名字一樣,這個方式分為“標記”和“清除”兩個階段,首先對不被使用的物件新增一個標記,之後對所有標記到的物件進行統一回收。
缺點:
- 標記和清除兩個階段的效率都不高
- 在回收之後會產生大量不連續的記憶體碎片,導致以後難以儲存較大的物件
- 複製演演算法 將物件分為兩塊,當一塊物件用完了,就將還在使用的物件複製到另一塊物件上去。較 標記-清除演演算法 有更高的效率 缺點:每次只能使用一塊記憶體,使記憶體的利用率變低了。
- 標記-整理演演算法 前半部分和 標記-清除演演算法 一樣,但後續將所有存活的物件移向一端,清除了記憶體碎片。
- 分代收集演演算法 根據物件的不同存活週期,一般把物件分為新生代和老年代,根據各個年代的特點採用不同的收集演演算法。 對於新生代,每次都有大量物件死去,故採用複製演演算法。 對於老年代,物件存活率高,採用 標記-清除 或 標記-整理 演演算法。
垃圾收集器
如果說收集演演算法是記憶體回收的方法論,那麼垃圾收集器就是記憶體回收的具體實現。
- Serial 一個最基本、發展歷史最悠久的收集器。採用單執行緒的收集方式,且在收集時必須暫停其他所有的工作執行緒,直到收集結束。在 Client 模式下有較好效果。
- ParNew ParNew 收集器就是 Serial 的多執行緒版本,能與 CMS 配合工作。
- Parallel Scavenge 吞吐量優先的收集器。(吞吐量=使用者程式碼執行時間/(使用者程式碼執行時間+垃圾收集時間))
- Serial Old Serial 的老年代版本
- Parallel Old Parallel Scavenge 的老年代版本,JDK1.7、JDK1.8 中以 Parallel Scavenge + Parallel Old 為預設的新生代、老年代回收器。
- CMS 以最短回收停頓時間為目的,對 CPU 資源敏感
- G1 JDK1.9 中的預設垃圾收集器,G1 的主要關注點在於達到可控的停頓時間,在這個基礎上儘可能提高吞吐量。G1 中每個塊也會充當 Eden、Survivor、Old 三種角色,但是它們不是固定的,這使得記憶體使用更加地靈活。
記憶體分配策略
在 HotSpot 中,大多數情況下記憶體被分為新生代和老年代,預設分配比例為 1:2。在新生代中又被分為一個 Eden 和兩個 Survivor ,分配比例為 8:1:1。 一個新的物件一般會在新生代 Eden 區中分配。當 Eden 區沒有足夠空間進行分配時,將發起一次 Minor GC。 對於大物件(大量連續記憶體空間的Java物件),會直接進入老年代。 長期存活的物件(預設熬過 15 次 Minor GC),會進入老年代。 如果在 Survivor 中相同年齡的物件超過了 Survivor 的一般,這些物件將會直接進入老年代。
JDK命令列
一些用於監視虛擬機器器狀態和故障處理的命令
命令 | 作用 |
---|---|
jps | 顯示系統內所有虛擬機器器程式 |
jstat | 用於收集虛擬機器器各方面執行資料 |
jinfo | 顯示虛擬機器器配置資訊 |
jmap | 生成虛擬機器器記憶體轉儲快照(heapdump檔案) |
jhat | 用於分析 heapdump 檔案 |
jstack | 顯示虛擬機器器執行緒快照 |