java垃圾回收

關於垃圾回收,首先要解決的問題是 如何知道哪些對象須要回收java

目前有兩種算法算法

1 引用計數法多線程

每一個對象上都有一個引用計數,對象每被引用一次,引用計數器就+1,對象引用被釋放,引用計數器-1,直到對象的引用計數爲0,對象就標識能夠回收,併發

可是這個算法有明顯的缺陷,對於循環引用的狀況下,循環引用的對象就不會被回收jvm

2 root搜索算法spa

在java中,可做爲GC Roots的對象包括下面幾種:線程

  • 虛擬機棧中的引用對象
  • 方法區中的類靜態屬性引用的對象
  • 方法區的常量引用的對象
  • 本地方法棧JNI的引用對象

Jvm在肯定是否回收的對象的時候採用的是root搜索算法來實現。code

 

在root搜索算法的裏面,咱們說的引用這裏都指定的是強引用關係。所謂強引用關係,就是經過用new 方式建立的對象,而且顯示關聯的對象對象

Object obj = new Object();

以上就是表明的是強引用關係,變量obj 強引用了 Object的一個對象。生命週期

java裏面有四種應用關係,從強到弱分別爲:

Strong Reference(強引用) –>Weak Reference (弱引用) -> Soft Reference(軟引用) – > Phantom Reference(引用)

 

Strong Reference : 只有在引用對象root不可達的狀況下才會標識爲可回收,垃圾回收纔可能進行回收

Weak Reference :即便在root算法中 其引用的對象root可達到,可是若是jvm堆內存 不夠的時候,仍是會被回收,jkd1.2 後,WeakReference類實現弱引用

Soft Reference : 不管其引用的對象是否root可達,在響應內存須要時,由垃圾回收判斷是否須要回收,jkd1.2 後,softReference類實現弱引用

Phantom Reference :在回收器肯定其指示對象可另外回收以後,被加入垃圾回收隊列,jkd1.2 後,PhantomReference類實現弱引用

 

二 典型的垃圾回收分算法

 

  1.  Mark-Sweep(標記-清除)算法

  這是最基礎的垃圾回收算法,之因此說它是最基礎的是由於它最容易實現,思想也是最簡單的。標記-清除算法分爲兩個階段:標記階段和清除階段。標記階段的任務是標記出全部須要被回收的對象,清除階段就是回收被標記的對象所佔用的空間。

  標記-清除算法實現起來比較容易,可是有一個比較嚴重的問題就是容易產生內存碎片,碎片太多可能會致使後續過程當中須要爲大對象分配空間時沒法找到足夠的空間而提早觸發新的一次垃圾收集動做。

  2.  Copying(複製)算法

  爲了解決Mark-Sweep算法的缺陷,Copying算法就被提了出來。它將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另一塊上面,而後再把已使用的內存空間一次清理掉,這樣一來就不容易出現內存碎片的問題。

  這種算法雖然實現簡單,運行高效且不容易產生內存碎片,可是卻對內存空間的使用作出了高昂的代價,由於可以使用的內存縮減到原來的一半。

  很顯然,Copying算法的效率跟存活對象的數目多少有很大的關係,若是存活對象不少,那麼Copying算法的效率將會大大下降。

  3.  Mark-Compact(標記-整理)算法

  爲了解決Copying算法的缺陷,充分利用內存空間,提出了Mark-Compact算法。該算法標記階段和Mark-Sweep同樣,可是在完成標記以後,它不是直接清理可回收對象,而是將存活對象都向一端移動,而後清理掉端邊界之外的內存

  4.  Generational Collection(分代收集)算法

  分代收集算法是目前大部分JVM的垃圾收集器採用的算法。它的核心思想是根據對象存活的生命週期將內存劃分爲若干個不一樣的區域。通常狀況下將堆區劃分爲老年代(Tenured Generation)和新生代(Young Generation),老年代的特色是每次垃圾收集時只有少許對象須要被回收,而新生代的特色是每次垃圾回收時都有大量的對象須要被回收,那麼就能夠根據不一樣代的特色採起最適合的收集算法。

  目前大部分垃圾收集器對於新生代都採起Copying算法,由於新生代中每次垃圾回收都要回收大部分對象,也就是說須要複製的操做次數較少,可是實際中並非按照1:1的比例來劃分新生代的空間的,通常來講是將新生代劃分爲一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden空間和其中的一塊Survivor空間,當進行回收時,將Eden和Survivor中還存活的對象複製到另外一塊Survivor空間中,而後清理掉Eden和剛纔使用過的Survivor空間。

  而因爲老年代的特色是每次回收都只回收少許對象,通常使用的是Mark-Compact算法。

  注意,在堆區以外還有一個代就是永久代(Permanet Generation),它用來存儲class類、常量、方法描述等。對永久代的回收主要回收兩部份內容:廢棄常量和無用的類。

 

 三 .典型的垃圾收集器

 

垃圾收集算法是 內存回收的理論基礎,而垃圾收集器就是內存回收的具體實現。下面介紹一下HotSpot(JDK 7)虛擬機提供的幾種垃圾收集器

 

如圖展現了不一樣分代的收集器,若是兩個收集器間存在連線,就說明他們能夠搭配使用。

  1.Serial/Serial Old

  Serial/Serial Old收集器是最基本最古老的收集器,它是一個單線程收集器,而且在它進行垃圾收集時,必須暫停全部用戶線程。Serial收集器是針對新生代的收集器,採用的是Copying算法,Serial Old收集器是針對老年代的收集器,採用的是Mark-Compact算法。它的優勢是實現簡單高效,可是缺點是會給用戶帶來停頓。

  2.ParNew

  ParNew收集器是Serial收集器的多線程版本,使用多個線程進行垃圾收集。

  3.Parallel Scavenge

  Parallel Scavenge收集器是一個新生代的多線程收集器(並行收集器),它在回收期間不須要暫停其餘用戶線程,其採用的是Copying算法,該收集器與前兩個收集器有所不一樣,它主要是爲了達到一個可控的吞吐量。

  4.Parallel Old

  Parallel Old是Parallel Scavenge收集器的老年代版本(並行收集器),使用多線程和Mark-Compact算法。

  5.CMS

  CMS(Current Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器,它是一種併發收集器,採用的是Mark-Sweep算法。(其中初始標記和從新標記這兩個步驟須要stop the world,併發標記都和併發清除的過程當中,收集器線程能夠與用戶線程一塊兒工做)

  6.G1

  G1收集器是當今收集器技術發展最前沿的成果,它是一款面向服務端應用的收集器,它能充分利用多CPU、多核環境。所以它是一款並行與併發收集器,而且它能創建可預測的停頓時間模型。

 四 內存分配

 

  Java體系中所提倡的自動內存管理最終能夠歸結爲自動化的解決兩個問題:給對象分配內存以及回收分配給對象的內存,關於回收內存的這點,如上總結,如今咱們一塊兒探討一下如何給對象分配內存。

  分配的規則並非百分百固定的,其細節取決於當前使用哪一種垃圾收集器組合,還有虛擬機中內存相關的參數設置。

  •  大多數狀況下,對象新生代Eden區中分配
  •  大對象直接進入老年代,虛擬機提供了一個-XX:PretenureSizeThreshold 參數,令大於這個設置值的對象直接在老年代中分配,這樣作的目的是避免在Eden區和兩個Survivor     區之間發生大量的內存拷貝。
  •    長期存活的對象將進入老年代,對象晉升老年代的年齡閾值,能夠經過-XX:MaxTenuringThreshold 來設置。

注:本篇內容整理自《深刻理解Java虛擬機》

相關文章
相關標籤/搜索