JVM垃圾回收器理論分析與詳解【純理論】

繼續上次【http://www.javashuo.com/article/p-ctjyidxk-cy.html】的理論繼續。。有點吐血的感受,都不知道學了這麼一大堆理論有何實際意義,自己JVM就是個理論體系比較多的東東,因此理論不得不去面對,繼續硬着頭皮往前進。html

內存結構程序員

這個在以前的學習中都已經學習過了,複習一下。web

內存分配算法

  • 堆上分配
    大多數狀況在eden【年輕代中的一個區域】上分配,偶爾會直接在old【老年代】上分配,細節取決於GC的實現。
  • 棧上分配
    原子類型的局部變量。

內存回收安全

一、GC要作的是將那些dead的對象所佔用的內存回收掉。數據結構

  • Hotspot認爲沒有引用的對象是dead的。
  • Hotspot將引用分爲四種:Strong【強引用】、Soft【軟引用】、Weak【弱引用】、Phantom【虛引用】,這是大夥熟知的。
    一、Strong既默認經過Object o = new Object()這種方式賦值的引用。
    二、Soft、Weak、Phantom這三種則是繼續Reference。

二、在Full GC時會對Reference類型的引用進行特殊處理。多線程

  • Soft:內存不夠時必定會被GC、長期不用也會被GC。
  • Weak:必定會被GC,當被mark爲dead,會在ReferenceQueue中通知。
  • Phantom:原本就沒引用,當從jvm heap中釋放時會通知。

以上的概念會在將來舉例進行代碼說明的,先有個印象。併發

垃圾收集算法dom

以上是一些比較經典的垃圾收集算法,下面會逐個進行說明。jvm

GC的時機

一、在分代模型的基礎上,GC從時機上分爲兩種:Scavenge GC和Full GC。

二、Scavenge GC(Minor GC)

  • 觸發時機:新對象生成時,Eden空間滿了。
  • 理論上Eden區大多數對象會在Scavenge GC回收,複製算法的執行效率會很高,Scavenge GC時間比較短。

三、Full GC【這個在實際中必定得要避免】

  • 對整個JVM進行整理,包括Young、Old和Perm。
  • 主要的觸發時機:1)Old滿了;2)Perm滿了;3)system.gc()
  • 效率很低,儘可能減小Full GC

垃圾回收器(Garbage Collector)

  • 分代模型:GC的宏觀願景。
  • 垃圾回收器:GC的具體實現。
  • Hotspot JVM提供多種垃圾回收器,咱們須要根據具體應用的須要採用不一樣的回收器。
  • 沒有萬能的垃圾回收器,每種垃圾回收器都有本身的適用場景。

垃圾收集器的「並行」和「併發」

  • 並行(Parallel):指多個收集器的線程同時工做,可是用戶線程處於等待狀態。
  • 併發(Concurrent):指收集器在工做時同時,能夠容許用戶線程工做。
    併發不表明解決了GC停頓的問題,在關鍵的步驟仍是要停頓。好比在收集器標記垃圾的時候。但在清除垃圾的時候,用戶線程能夠和GC線程併發執行。 

Serial收集器

  • 單線程收集器,收集時會暫停全部工做線程(Stop The World,簡單STW),使用複製收集算法,虛擬機運行在Client模式時的默認新生代會採用此收集器。
  • 最先的收集器,單線程進行GC。
  • New和Old Generation均可以使用。
  • 在新生代,採用複製算法:在老年代,採用Mark-Compact算法。
  • 由於是單線程GC,沒有多線程切換的額外開銷,簡單實用。
  • Hotspot Client模式缺省的的收集器

    如圖中出現了一個詞:「Safepoint」,安全點,在以後會舉具體的實例來講明安全點的做用。

ParNew收集器

  • ParNew收集器就是Serial的多線程版本,除了使用多個收集線程外,其他行爲包括算法、STW、對象分配規則、回收策略等都與Serial收集器如出一轍。
  • 對應的這種收集器是虛擬機運行在Server模式的默認新生代收集器,在單CPU的環境中,ParNew收集器並不會比Serial收集器有更好的效果。
  • Serial收集器在新生代的多線程版本。
  • 使用複製算法(由於針對新生代)。
  • 只有在多CPU的環境下,效率纔會比Serial收集器高。
  • 能夠經過-XX:ParallelGCThreads來控制GC線程數的多少。須要結合具體CPU的個數。
  • Server模式下新生代的缺省收集器。

Parallel Scavenge收集器

  •  Parallel Scavenge收集器也是一個多線程收集器,也是使用複製算法,但它的對象分配規則與回收策略都與ParNew收集器有所不一樣,它是以吞吐量最大化(既GC時間佔總運行時間最小)爲目標的收集器實現,它容許較長時間的STW換取總吞吐量最大化。

Serial Old收集器

  •  Serial Old是單線程收集器,使用標記-整理算法,是老年代的收集器。

Parallel Old收集器

  • 老年代版本吞吐量優先收集器,使用多線程和標記一整理算法,JVM1.6提供,在此以前,新生代使用了PS收集器的話,老年代除Serial Old外別無選擇,由於PS沒法與CMS收集器配合工做。【瞭解既可】
  • Parallel Scavenge在老年代的實現
  • 在JVM1.6纔出現Parallel Old
  • 採用多線程,Mark-Compact算法
  • 更注重吞吐量
  • Parallel Scavenge + Parallel Old = 高吞吐量,但GC停頓可能不理想

CMS(Concurrent Mark Sweep)收集器【特別複雜的一種收集器】

  • CMS是一種以最短停頓時間爲目標的收集器,使用CMS並不能達到GC效率最高(整體GC時間最小),但它能儘量下降GC時服務的停頓時間,CMS收集器使用的是標記-清除算法。
  • 追求最短停頓時間,很是適合Web應用。
  • 只針對老年區,通常結合ParNew使用。
  • Concurrent,GC線程和用戶線程併發工做(儘可能併發)。
  • Mark-Sweep。
  • 只有在多CPU環境下才有意義 。
  • 使用-XX:+UseConcMarkSweepGC打開。
  • CMS以犧牲CPU資源的代價來減小用戶線程的停頓。當CPU個數少於4的時候,有可能對吞吐量影響很是大。
  • CMS在併發清理的過程當中,用戶線程還在跑。這時候須要預留一部分空間給用戶線程。
  • CMS用Mark-Sweep,會帶來碎片問題。碎片過多的時候會容易頻繁觸發Full GC。

GC垃圾收集器的JVM參數定義

Java內存泄漏的經典緣由

一、對象定義在錯誤的範圍(Wrong Scope)。

  • 若是Foo實例對象的生命較長,會致使臨時性內存泄漏。(這裏的names變量其實只是臨時做用)
  • JVM喜歡生命週期短的對象,這樣作已經足夠高效【調整】

    這樣一改以後,只要是doIt()方法一結束names的臨時變量就立馬會被回收。

二、異常(Exception)處理不當。

  • 錯誤的作法

    對於有經驗的程序員應該不會出現上面的問題,可是這裏只是拋出泄漏的場景。
  • 正確的作法

三、集合數據管理不當。

  • 當使用Array-based的數據結構(ArrayList,HashMap等)時,儘可能減小resize:
    a、好比new ArrayList時,儘可能估算size,在建立的時候把size肯定。
    b、減小resize能夠避免沒有必要的array copying,gc碎片等問題。
  • 若是一個List只須要順序訪問,不須要隨機訪問(Random Access),用LinkedList代替ArrayList
    a、LInkedList本質是鏈表,不須要resize,但只適用於順序訪問。

 

以上是對JVM垃圾回收相關理論的總體瞭解,說實話看完其實頭暈暈的,不要緊,接下來則會用實踐來反證理論。

相關文章
相關標籤/搜索