1. 程式人生 > >JVM——Java內存區域相關3

JVM——Java內存區域相關3

用戶交互 類型 預測 對象 連續 可控 必須 現在 cnblogs

一. 垃圾收集算法

1. 標記-清除算法

缺點:1.效率低,標記和清除兩個過程的效率都不高;

2.空間問題,標記清除後會產生大量的不連續內存碎片。

2. 復制算法

將內存分成三塊:一塊較大的Eden和兩塊較小的Survivor空間。每次使用Eden和其中一塊Survivor,回收後將Eden和Survivor中還存活著的對象一次性的復制到另外一塊Survivor空間上。如果復制過程中Survivor的空間不夠用時,需要依賴老年代進行分配擔保。

3. 標記-整理算法

和標記-清理算法類似,只不過後續步驟不是直接對可回收對象進行清理,而是讓所有存貨對象都向一邊移動,然後直接清理掉端邊界以外的內存,這樣就保證了沒有內存碎片的存在。這種方法比較適合老年代使用。

二. HotSpot的算法實現

1. 枚舉根節點

準確式GC(即虛擬機知道哪塊內存存放對象的具體類型),為了避免一個不漏的檢查完所有執行上下文和全局的引用位置,虛擬機通過一組稱為OopMap解決了分辨獲取對象類型信息的問題。

2. 安全點

能夠改變對象引用的指令非常多,如果針對每一條指令都生成對象的OopMap將會耗費大量的額外空間。為了解決這個問題,虛擬機只在特點的地方——一些“具有讓程序長時間執行”的地方(為了將GC控制在一個合適的頻率)如方法調用、循環跳轉、異常跳轉——這些地方被稱為Safepoint。

為了同步所有線程,讓他們都到達Safepoint之後再統一GC,有兩種發放可供選擇:

  • 搶先式中斷,先把所有線程全部中斷,不再安全點上就恢復線程,讓它跑到安全點上。
  • 主動式,GC需要中斷時設置一個標誌,各線程會去主動輪詢這個標誌,當發現中斷標誌就自己中斷掛起,輪詢標誌的地方和安全點是重合的。

疑問:為什麽不是每次到達安全點再輪詢?然後搶先中斷方法的恢復線程後讓它跑到安全點的過程是否和主動式有些類似??

3. 安全區域

在程序不執行即處於sleep狀態或者blocked狀態時無法響應JVM的中斷請求、“走”到安全點掛起。這時候就需要安全區域(Safe Region)來解決。

Safe Region是指在一段代碼片段中引用關系都不會發生變化,在這個區域中的任意地方開始GC都是安全的。在線程執行到Safe Region中的代碼時,首先標識自己已經進入了Safe Region,那麽當這段時間JVM要發起GC時,就不用管標識自己為Safe Region狀態的線程了。在線程要離開Safe Region時,它要檢查系統是否已經完成了根節點枚舉(或者整個GC過程),如果完成了,那線程就繼續執行,否則它就等到直到收到可以安全離開Safe Region的信號為止。

疑問:這個真的解決了線程阻塞而無法響應JVM中斷請求的問題嗎?

三. 垃圾收集器

1. Serial(new:Serial——old:CMS、Serial Old)

單線程收集器,進行垃圾收集時必須暫停其他所有的工作線程直到它收集結束。

2. ParNew(new:ParNew——old:CMS、Serial Old)

Serial收集器的多線程版本,能與CMS配合工作是選擇ParNew的一個重要原因。-XX:ParallelGCThreads參數來限制垃圾收集的線程數。

3. Parallel Scavenge(new:Parallel Scavenge——old:Serial Old、Parallel Old)

CMS等收集器的關註點是盡可能縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控的吞吐量(Throughput)。

停頓時間越短就越適合需要與用戶交互的程序,相應更快;高吞吐量則可以高效率的利用CPU時間,盡快完成程序的計算任務。

相關參數

4. Serial Old(new:Serial、ParNew、Parallel Scavenge——old:Serial Old)

單線程收集器。

5. Parallel Old(new:Parallel Scavenge——old:Parallel Old)

在主要吞吐量以及CPU資源敏感的場合,可以優先考慮Parallel Scavenge加Parallel Old收集器組合。

6. CMS(new:Serial、ParNew——old:CMS、Serial Old)

  • 初始標記(stop the world)
  • 並發標記
  • 重新標記(stop the world)
  • 並發清理

缺點:

      • 對CPU資源敏感。在並發階段,雖然它不會導致用戶線程停頓,但是會因為占用了一部分線程(CPU資源)而導致應用程序變慢。
      • CMS無法處理浮動垃圾(Floating Garbage)。因為CMS並發清理階段用戶線程還在運行,所以伴隨著程序運行還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後而只能留待下一次GC時再處理。這部分垃圾就稱為浮動垃圾。另外一個相應的後果時CMS不能像其他線程一樣等到老年代幾乎被完全填滿了在進行收集,因為它必須留一部分給垃圾收集時程序運行使用,所以CMS有一個老年代閥值,超過這個閥值就進行收集。
      • CMS是一個基於“標記-清理”算法實現的收集器,會產生內存碎片

7. G1

  • 並發與並行
  • 分代收集
  • 空間整合:基於“標記-清理”,沒有內存碎片問題
  • 可預測的停頓:消耗在垃圾收集上的時間不得超過N毫秒

使用G1收集器,Java堆的內存布局就與其他收集器有很大差別,它將整個Java堆劃分多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔離的,他們都是一部分Region(不需要連續)的集合。

G1可預測的停頓實現歸功於避免進行全區域的垃圾收集。G1每次只收集回報最高的Region。

Region之間的對象引用虛擬機都是使用Rememberd Set來避免全堆掃描的。

JVM——Java內存區域相關3