1. 程式人生 > >java的記憶體回收機制

java的記憶體回收機制

如何確定一個物件是否可以被回收?

1、 引用計數演算法:判斷物件的引用數量

2、 可達性分析演算法:判斷物件到GC Root引用鏈是否可達

ps:c++中解決迴圈引用的方法:

1. 當只剩下最後一個引用的時候需要手動打破迴圈引用釋放物件。2. 當A的生存期超過B的生存期的時候,B改為使用一個普通指標指向A。3. 使用弱引用的智慧指標打破這種迴圈引用。 雖然這三種方法都可行,但方法1和方法2都需要程式設計師手動控制,麻煩且容易出錯。我們一般使用第三種方法:弱引用的智慧指標weak_ptr。

Java中引用的四種類型:

1.強引用:類似Object obj=new Object(),只要強引用還存在垃圾收集器永遠不會回收掉被引用的物件

2.軟引用:用來描述一些還有用但並非必須的物件,在系統將要發生記憶體溢位異常之前將會把這些物件列入回收範圍之中,JDK1.2之後通過SoftReference類實現軟引用

3.弱引用:描述非必須物件,比軟引用弱,被弱引用關聯的物件只能生成到下一次垃圾回收發生之前,無論記憶體是否足夠。通過WeakReference來實現

4.虛引用:最弱的引用關係,一個物件是否有虛引用存在完全不會對其生存時間構成影響,也無法通過虛引用來取得一個物件例項,虛引用的唯一目的就是在這個物件被回收時可以得到系統通知。通過PhantomReference類實現虛引用。

PS:即使在可達性分析演算法中不可達的物件也不是非死不可的。第一次不可達後會對物件進行一次標記和篩選,篩選物件沒有覆蓋finalize()方法或者已經被呼叫過,如果沒有那麼這個物件會被放置在F-Queue佇列中,在稍後由一個自動建立的低優先順序Finalizer執行緒去執行finalize()方法。稍後GC將會對佇列中的物件進行第二次小規模標記,如果在finalize()方法中重新與引用鏈上的物件關聯則可以移出即將回收的集合

可以做GC Root的物件

虛擬機器棧中的引用物件 方法區中類靜態屬性引用的物件 方法區中常量引用物件 本地方法棧中JNI引用物件

垃圾收集演算法

1、標記清除演算法

2、複製演算法

3、標記整理演算法

4、分代收集演算法

垃圾收集器

  • Serial收集器(複製演算法)新生代單執行緒收集器,標記和清理都是單執行緒,優點是簡單高效;

  • Serial Old收集器 (標記-整理演算法)老年代單執行緒收集器,Serial收集器的老年代版本;

  • ParNew收集器 (複製演算法)新生代收並行集器,實際上是Serial收集器的多執行緒版本,在多核CPU環境下有著比Serial更好的表現;

  • Parallel Scavenge收集器 (複製演算法)新生代並行收集器追求高吞吐量,高效利用 CPU。吞吐量 = 使用者執行緒時間/(使用者執行緒時間+GC執行緒時間),高吞吐量可以高效率的利用CPU時間,儘快完成程式的運算任務,適合後臺應用等對互動相應要求不高的場景;

  • Parallel Old收集器 (標記-整理演算法): 老年代並行收集器吞吐量優先,Parallel Scavenge收集器的老年代版本;

  • CMS(Concurrent Mark Sweep)收集器(標記-清除演算法): 老年代並行收集器以獲取最短回收停頓時間為目標的收集器,具有高併發、低停頓的特點,追求最短GC回收停頓時間。

  • G1(Garbage First)收集器 (標記-整理演算法): Java堆並行收集器,G1收集器是JDK1.7提供的一個新收集器,G1收集器基於“標記-整理”演算法實現,也就是說不會產生記憶體碎片。此外,G1收集器不同於之前的收集器的一個重要特點是:G1回收的範圍是整個Java堆(包括新生代,老年代),而前六種收集器回收的範圍僅限於新生代或老年代

PS:CMS和G1前3次標記的作用分別是

1.初始標記:僅標記GC Root能直接關聯到的物件

2.併發標記:進行可達性分析標記

3.重新標記:修正在併發標記期間使用者程式運作導致標記變動的物件

新生代轉移到老年代的觸發條件:

1、長期存活的物件

2、大物件直接進入老年代

3、minor gc後,survivor仍然放不下

4、動態年齡判斷 ,大於等於某個年齡的物件超過了survivor空間一半 ,大於等於某個年齡的物件直接進入老年代 

GC 相關引數總結

1. 與序列回收器相關的引數

-XX:+UseSerialGC:在新生代和老年代使用序列回收器。

-XX:+SurvivorRatio:設定 eden 區大小和 survivor 區大小的比例。

-XX:+PretenureSizeThreshold:設定大物件直接進入老年代的閾值。當物件的大小超過這個值時,將直接在老年代分配。

-XX:MaxTenuringThreshold:設定物件進入老年代的年齡的最大值。每一次 Minor GC 後,物件年齡就加 1。任何大於這個年齡的物件,一定會進入老年代。

2. 與並行 GC 相關的引數

-XX:+UseParNewGC: 在新生代使用並行收集器。

-XX:+UseParallelOldGC: 老年代使用並行回收收集器。

-XX:ParallelGCThreads:設定用於垃圾回收的執行緒數。通常情況下可以和 CPU 數量相等。但在 CPU 數量比較多的情況下,設定相對較小的數值也是合理的。

-XX:MaxGCPauseMills:設定最大垃圾收集停頓時間。它的值是一個大於 0 的整數。收集器在工作時,會調整 Java 堆大小或者其他一些引數,儘可能地把停頓時間控制在 MaxGCPauseMills 以內。

-XX:GCTimeRatio:設定吞吐量大小,它的值是一個 0-100 之間的整數。假設 GCTimeRatio 的值為 n,那麼系統將花費不超過 1/(1+n) 的時間用於垃圾收集。

-XX:+UseAdaptiveSizePolicy:開啟自適應 GC 策略。在這種模式下,新生代的大小,eden 和 survivor 的比例、晉升老年代的物件年齡等引數會被自動調整,以達到在堆大小、吞吐量和停頓時間之間的平衡點。

3. 與 CMS 回收器相關的引數

-XX:+UseConcMarkSweepGC: 新生代使用並行收集器,老年代使用 CMS+序列收集器。

-XX:+ParallelCMSThreads: 設定 CMS 的執行緒數量。

-XX:+CMSInitiatingOccupancyFraction:設定 CMS 收集器在老年代空間被使用多少後觸發,預設為 68%。

-XX:+UseFullGCsBeforeCompaction:設定進行多少次 CMS 垃圾回收後,進行一次記憶體壓縮。

-XX:+CMSClassUnloadingEnabled:允許對類元資料進行回收。

-XX:+CMSParallelRemarkEndable:啟用並行重標記。

-XX:CMSInitatingPermOccupancyFraction:當永久區佔用率達到這一百分比後,啟動 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。

-XX:UseCMSInitatingOccupancyOnly:表示只在到達閾值的時候,才進行 CMS 回收。

-XX:+CMSIncrementalMode:使用增量模式,比較適合單 CPU。

4. 與 G1 回收器相關的引數

-XX:+UseG1GC:使用 G1 回收器。

-XX:+UnlockExperimentalVMOptions:允許使用實驗性引數。

-XX:+MaxGCPauseMills:設定最大垃圾收集停頓時間。

-XX:+GCPauseIntervalMills:設定停頓間隔時間。

5. 其他引數

-XX:+DisableExplicitGC: 禁用顯示 GC。