JVM 調優和垃圾回收器說明
JVM垃圾收集演算法
JVM垃圾收集演算法有四種:標記-清除演算法、複製演算法、標記-整理演算法、分代收集演算法
標記-清除演算法:
該演算法如同它的名字一樣,分為兩個階段:標記、清除。首先標記出所有需要回收的物件,然後,統一清除這些被標記的物件。該演算法的缺點是:1、效率不高;2、產生大量不連續的記憶體碎片,導致有大量記憶體剩餘的情況下,由於,沒有連續的空間來存放較大的物件,從而觸發了另一次垃圾收集動作。
複製演算法:
由於標記-清除演算法的效率不高,從而提出了複製演算法。複製演算法將可用的記憶體分成兩樣大小的兩塊,每次只使用其中一塊記憶體。當這塊記憶體用完之後,就把還存活的物件複製到另外一塊上面,然後,把這塊清空。複製演算法克服了標記-清除演算法的兩個缺點,但是太浪費記憶體,相當於記憶體空間減小了一半。
隨著時間的積累,現在使用的複製演算法的虛擬機器,不再是把記憶體分為1:1的兩塊。因為98%的物件是壽命很短的,建立之後,很快就被回收了,存活下來的只有2%,所以,用來儲存存活物件的記憶體區,可以小一些。現在的商業虛擬機器是把可用記憶體分為一個較大的Eden空間和兩個較小的Survivor空間,每次使用Eden和其中的一塊Survivor。當回收時,把Eden和Survivor中的存活物件一次複製到另一塊Survivor記憶體區上,然後把Eden和剛才用過的Survivor空間清空。HotSpot虛擬機器預設Eden和Survivor的大小比例是8:1,這樣,每次新產生的物件可以使用90%的記憶體空間。
標記-整理演算法
從名字可以看出,該演算法是對“標記-清除”演算法的改進升級版。同樣的該演算法分為兩個階段:標記、整理。標記階段同“標記-清除”演算法。整理階段,不是直接對標記物件進行清理,而是讓所有存活的物件都移動到一端,然後,直接把邊界以外的記憶體清空。這就解決了“標記-清除”演算法會造成大量不連續記憶體碎片的問題。
分代收集演算法
分代收集演算法是根據物件的存活週期的不同,將記憶體劃分為幾塊。當前的商業虛擬機器的垃圾收集都採用了該演算法。一般把Java堆分成新生代(年輕代)和老年代(年老代)。這樣就可以根據各年代中物件的存活週期來選擇最合適的收集演算法了。新生代,由於只有少量的物件能存活下來,所以選用“複製演算法”,只需要付出少量存活物件的複製成本。老年代,由於物件的存活率高,沒有額外的空間分擔,就必須使用“標記-清除”或“標記-整理”演算法。
JVM垃圾收集器
由於記憶體中的物件,是按存活週期存放在不同的記憶體塊中的,所以,我們選擇不同的演算法來針對不同的記憶體塊進行垃圾收集。從而,對於,不同的記憶體塊,我們需要有不同的垃圾收集器。
新生代的垃圾收集器有:Serial收集器、ParNew收集器、Parallel Scavenge收集器
老年代的垃圾收集器有:Serial Old收集器、Parallel Old收集器、CMS收集器、G1收集器
下面我們來分別介紹一下這些垃圾收集器
Serial收集器/Serial Old收集器
Serial收集器/Serial Old收集器,是單執行緒的,使用“複製”演算法。當它工作時,必須暫停其它所有工作執行緒。特點:簡單而高效。對於執行在Client模式下的虛擬機器來說是一個很好的選擇。
ParNew收集器
ParNew收集器,是Serial收集器的多執行緒版。是執行在Server模式下的虛擬機器中首選的新生代收集器。除了Serial收集器外,目前只有它能與CMS收集器配合工作。
Parallel Scavenge收集器/Parallel Old收集器
Parallel Scavenge收集器,也是使用“複製”演算法的、並行的多執行緒收集器。這些都和ParNew收集器一樣。但它關注的是吞吐量(CPU用於執行使用者程式碼的時間與CPU總消耗時間的比值),而其它收集器(Serial/Serial Old、ParNew、CMS)關注的是垃圾收集時使用者執行緒的停頓時間。
Parallel Old收集器是Parallel Scavenge收集器的老年代版本。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器,使用“標記-清除”演算法。
CMS收集器分4個步驟進行垃圾收集工作:
1、初始標記 2、併發標記 3、重新標記 4、併發清除
其中“初始標記”、“重新標記”是需要暫停其它所有工作執行緒的。
G1收集器
G1(Garbage First)收集器,基於“標記-整理”演算法,可以非常精確地控制停頓。
問題
當我們的程式碼出現下面的情形時,該演算法將無法適應
a)ObjA.obj = ObjB
b)ObjB.obj - ObjA
這樣的程式碼會產生如下引用情形 objA指向objB,而objB又指向objA,這樣當其他所有的引用都消失了之後,objA和objB還有一個相互的引用,也就是說兩個物件的引用計數器各為1,而實際上這兩個物件都已經沒有額外的引用,已經是垃圾了。
2、根搜尋演算法
根搜尋演算法是從離散數學中的圖論引入的,程式把所有的引用關係看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認為是沒有被引用到的節點,即無用的節點。
目前java中可作為GC Root的物件有
1、虛擬機器棧中引用的物件(本地變量表)
2、方法區中靜態屬性引用的物件
3、方法區中常量引用的物件
4、本地方法棧中引用的物件(Native物件)
說了這麼多,其實我們可以看到,所有的垃圾回收機制都是和引用相關的,那我們來具體的來看一下引用的分類,到底有哪些型別的引用?每種引用都是做什麼的呢?
Java中存在四種引用,每種引用如下:
1、強引用
只要引用存在,垃圾回收器永遠不會回收
Object obj = new Object();
//可直接通過obj取得對應的物件如obj.equels(new Object());
而這樣 obj物件對後面new Object的一個強引用,只有當obj這個引用被釋放之後,物件才會被釋放掉,這也是我們經常所用到的編碼形式。
2、軟引用
非必須引用,記憶體溢位之前進行回收,可以通過以下程式碼實現
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有時候會返回null
這時候sf是對obj的一個軟引用,通過sf.get()方法可以取到這個物件,當然,當這個物件被標記為需要回收的物件時,則返回null;
軟引用主要使用者實現類似快取的功能,在記憶體足夠的情況下直接通過軟引用取值,無需從繁忙的真實來源查詢資料,提升速度;當記憶體不足時,自動刪除這部分快取資料,從真正的來源查詢這些資料。
3、弱引用
第二次垃圾回收時回收,可以通過如下程式碼實現
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有時候會返回null
wf.isEnQueued();//返回是否被垃圾回收器標記為即將回收的垃圾
弱引用是在第二次垃圾回收時回收,短時間內通過弱引用取對應的資料,可以取到,當執行過第二次垃圾回收時,將返回null。
弱引用主要用於監控物件是否已經被垃圾回收器標記為即將回收的垃圾,可以通過弱引用的isEnQueued方法返回物件是否被垃圾回收器
4、虛引用(幽靈/幻影引用)
垃圾回收時回收,無法通過引用取到物件值,可以通過如下程式碼實現
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永遠返回null
pf.isEnQueued();//返回從記憶體中已經刪除
虛引用是每次垃圾回收的時候都會被回收,通過虛引用的get方法永遠獲取到的資料為null,因此也被成為幽靈引用。
虛引用主要用於檢測物件是否已經從記憶體中刪除。
在上文中已經提到了,我們的物件在記憶體中會被劃分為5塊區域,而每塊資料的回收比例是不同的,根據IBM的統計,資料如下圖所示:
我們知道,方法區主要存放類與類之間關係的資料,而這部分資料被載入到記憶體之後,基本上是不會發生變更的,
Java堆中的資料基本上是朝生夕死的,我們用完之後要馬上回收的,而Java棧和本地方法棧中的資料,因為有後進先出的原則,當我取下面的資料之前,必須要把棧頂的元素出棧,因此回收率可認為是100%;而程式計數器我們前面也已經提到,主要使用者記錄執行緒執行的行號等一些資訊,這塊區域也是被認為是唯一一塊不會記憶體溢位的區域。在SunHostSpot的虛擬機器中,對於程式計數器是不回收的,而方法區的資料因為回收率非常小,而成本又比較高,一般認為是“價效比”非常差的,所以Sun自己的虛擬機器HotSpot中是不回收的!但是在現在高效能分散式J2EE的系統中,我們大量用到了反射、動態代理、CGLIB、JSP和OSGI等,這些類頻繁的呼叫自定義類載入器,都需要動態的載入和解除安裝了,以保證永久帶不會溢位,他們通過自定義的類載入器進行了各項操作,因此在實際的應用開發中,類也是被經常載入和解除安裝的,方法區也是會被回收的!但是方法區的回收條件非常苛刻,只有同時滿足以下三個條件才會被回收!
1、所有例項被回收
2、載入該類的ClassLoader被回收
3、Class物件無法通過任何途徑訪問(包括反射)
好了,我們現在切入正題,Java1.2之前主要通過引用計數器來標記是否需要垃圾回收,而1.2之後都使用根搜尋演算法來收集垃圾,而收集後的垃圾是通過什麼演算法來回收的呢?
1、標記-清除演算法
2、複製演算法
3、標記-整理演算法
我們來逐一過一下
1、標記-清除演算法
標記-清除演算法採用從根集合進行掃描,對存活的物件物件標記,標記完畢後,再掃描整個空間中未被標記的物件,進行回收,如上圖所示。
標記-清除演算法不需要進行物件的移動,並且僅對不存活的物件進行處理,在存活物件比較多的情況下極為高效,但由於標記-清除演算法直接回收不存活的物件,因此會造成記憶體碎片!
2、複製演算法
複製演算法採用從根集合掃描,並將存活物件複製到一塊新的,沒有使用過的空間中,這種演算法當控制元件存活的物件比較少時,極為高效,但是帶來的成本是需要一塊記憶體交換空間用於進行物件的移動。也就是我們前面提到的
s0 s1等空間。
3、標記-整理演算法
標記
-整理演算法採用標記-清除演算法一樣的方式進行物件的標記,但在清除時不同,在回收不存活的物件佔用的空間後,會將所有的存活物件往左端空閒空間移動,並更新對應的指標。標記-整理演算法是在標記-清除演算法的基礎上,又進行了物件的移動,因此成本更高,但是卻解決了記憶體碎片的問題。
我們知道,JVM為了優化記憶體的回收,進行了分代回收的方式,對於新生代記憶體的回收(minor GC)主要採用複製演算法,下圖展示了minor GC的執行過程。
對於新生代和舊生代,
JVM可使用很多種垃圾回收器進行垃圾回收,下圖展示了不同生代不通垃圾回收器,其中兩個回收器之間有連線表示這兩個回收器可以同時使用。
而這些垃圾回收器又分為序列回收方式、並行回收方式合併發回收方式執行,分別運用於不同的場景。如下圖所示
下面我們來逐一介紹一下每個垃圾回收器。
1、Serial收集器
看名字我們都可以看的出來,這個屬於序列收集器。其執行示意圖如下
Serial
收集器是歷史最悠久的一個回收器,JDK1.3之前廣泛使用這個收集器,目前也是ClientVM下 ServerVM 4核4GB以下機器的預設垃圾回收器。序列收集器並不是只能使用一個CPU進行收集,而是當JVM需要進行垃圾回收的時候,需要中斷所有的使用者執行緒,知道它回收結束為止,因此又號稱“Stop The World”的垃圾回收器。注意,JVM中文名稱為java虛擬機器,因此它就像一臺虛擬的電腦一樣在工作,而其中的每一個執行緒就被認為是JVM的一個處理器,因此大家看到圖中的CPU0、CPU1實際為使用者的執行緒,而不是真正機器的CPU,大家不要誤解哦。
序列回收方式適合低端機器,是Client模式下的預設收集器,對CPU和記憶體的消耗不高,適合使用者互動比較少,後臺任務較多的系統。
Serial收集器預設新舊生代的回收器搭配為Serial+ SerialOld
2、ParNew收集器
ParNew收集器其實就是多執行緒版本的Serial收集器,其執行示意圖如下
同樣有
Stop The World的問題,他是多CPU模式下的首選回收器(該回收器在單CPU的環境下回收效率遠遠低於Serial收集器,所以一定要注意場景哦),也是Server模式下的預設收集器。
3、ParallelScavenge
ParallelScavenge又被稱為是吞吐量優先的收集器,器執行示意圖如下
ParallelScavenge
所提到的吞吐量=程式執行時間/(JVM執行回收的時間+程式執行時間),假設程式運行了100分鐘,JVM的垃圾回收佔用1分鐘,那麼吞吐量就是99%。在當今網路告訴發達的今天,良好的響應速度是提升使用者體驗的一個重要指標,多核並行雲端計算的發展要求程式儘可能的使用CPU和記憶體資源,儘快的計算出最終結果,因此在互動不多的雲端,比較適合使用該回收器。
4、ParallelOld
ParallelOld是老生代並行收集器的一種,使用標記整理演算法、是老生代吞吐量優先的一個收集器。這個收集器是JDK1.6之後剛引入的一款收集器,我們看之前那個圖之間的關聯關係可以看到,早期沒有ParallelOld之前,吞吐量優先的收集器老生代只能使用序列回收收集器,大大的拖累了吞吐量優先的效能,自從JDK1.6之後,才能真正做到較高效率的吞吐量優先。其執行示意圖如下
5、
SerialOld
SerialOld是舊生代Client模式下的預設收集器,單執行緒執行;在JDK1.6之前也是ParallelScvenge回收新生代模式下舊生代的預設收集器,同時也是併發收集器CMS回收失敗後的備用收集器。其執行示意圖如下
6、
CMS
CMS又稱響應時間優先(最短回收停頓)的回收器,使用併發模式回收垃圾,使用標記-清除演算法,CMS對CPU是非常敏感的,它的回收執行緒數=(CPU+3)/4,因此當CPU是2核的實惠,回收執行緒將佔用的CPU資源的50%,而當CPU核心數為4時僅佔用25%。他的執行示意圖如下
CMS
模式主要分為4個過程
在初始標記的時候,需要中斷所有使用者執行緒,在併發標記階段,使用者執行緒和標記執行緒
併發執行,而在這個過程中,隨著記憶體引用關係的變化,可能會發生原來標記的物件被釋放,進而引發新的垃圾,因此可能會產生一系列的浮動垃圾,不能被回收。
CMS 為了確保能夠掃描到所有的物件,避免在Initial Marking 中還有未標識到的物件,採用的方法為找到標記了的物件,並將這些物件放入Stack 中,掃描時尋找此物件依賴的物件,如果依賴的物件的地址在其之前,則將此物件進行標記,並同時放入Stack 中,如依賴的物件地址在其之後,則僅標記該物件。
在進行Concurrent Marking 時minor GC 也可能會同時進行,這個時候很容易造成舊生代物件引用關係改變,CMS 為了應對這樣的並發現象,提供了一個Mod Union Table 來進行記錄,在這個Mod Union Table中記錄每次minor GC 後修改了的Card 的資訊。這也是ParallelScavenge不能和CMS一起使用的原因。
CMS產生浮動垃圾的情況請見如下示意圖
相關推薦
JVM 調優和垃圾回收器說明
JVM垃圾收集演算法 JVM垃圾收集演算法有四種:標記-清除演算法、複製演算法、標記-整理演算法、分代收集演算法 標記-清除演算法: 該演算法如同它的名字一樣,分為兩個階段:標記、清除。首先標記出所有需要回收的物件,然後,統一
JVM調優(二)垃圾回收算法
rational 多線程處理 ref 對象復制 height 二階 原因 機器 問題 原文出處: pengjiaheng 可以從不同的的角度去劃分垃圾回收算法: 按照基本回收策略分 引用計數(Reference Counting): 比較古老的回收算法。原理是此對象有一個引
java中高級面試題, 虛擬機,JVM調優,垃圾回收,多線程,內存模型
mybatis mys set 內存 集合 實現 runable ringbuf mon 面試問題: 一、Java基礎方面: 1、Java面相對象的思想的理解(主要是多態): http://blog.csdn.net/zhaojw_420/article/details/7
JAVA JVM引數調優、以及回收器
[轉]JVM系列三:JVM引數設定、分析 不管是YGC還是Full GC,GC過程中都會對導致程式執行中中斷,正確的選擇不同的GC策略,調整JVM、GC的引數,可以極大的減少由於GC工作,而導致的程式執行中斷方面的問題,進而適當的提高Java程式的工作效率。但是調整GC是以個極為複雜的過程,由於
JVM參數調優與垃圾回收機制
相對 完全 相關 相同 cat 屬於 跟蹤系統 col 組成 自動內存管理機制 Java虛擬機原理 所謂虛擬機,就是一臺虛擬的機器。他是一款軟件,用來執行一系列虛擬計算指令,大體上虛擬機可以分為 系統虛擬機和程序虛擬機, 大名鼎鼎的Visual Box、Vmare就屬於系
java架構之路-(12)JVM垃圾回收演算法和垃圾回收器
接上次JVM虛擬機器堆記憶體模型來繼續說,上次我們主要說了什麼時候可能把物件直接放在老年代,還有我們的可能性分析,提出GCroot根的概念。這次我們主要來說說垃圾回收所使用的的演算法和我們的垃圾回收器,需要了解我們的可達性分析GCroot根是什麼,還有我們的動態年齡判斷和老年代分配擔保機制,還不清楚咋回事
JVM調優之垃圾定位、垃圾回收演算法、垃圾處理器對比
談垃圾回收器之前,要先講講垃圾回收演算法,以及JVM對垃圾的認定策略,JVM垃圾回收器是垃圾回收演算法的具體實現,瞭解了前面的前置知識,有利於對垃圾回收器的理解。 ## 什麼是垃圾? 垃圾,主要是指堆上的物件,那麼如何確定這些物件是可以被回收的呢? 大概思路就是,如果一個物件永遠不可能被訪問到,那麼
JVM GC算法 垃圾回收器
com 修正 可用 mark 信息 網站 最長 style 互聯網 JVM的垃圾回收算法有三種: 1.標記-清除(mark-sweep):啥都不說,直接上圖 2.標記-整理(mark-compact) 3.復制(copy) 分代收集算法
JVM調優 及 GC收集器
根據Java GC收集器具體分類,我們可以看出JVM根據需求不同提供了三種選擇:序列收集器、並行收集器、併發收集器。 序列收集器只適用於小資料量的情況,我們主要了解一下並行收集器和併發收集器。預設情況下,JDK5.0以前都是使用序列收集器,如果需要使用其他收集器需要在啟動的是時候加入相應的引
速記JVM記憶體模型和垃圾回收策略
一、常用JVM引數 -Xms: 初始堆大小 -Xmx: 最大堆 -Xss: 棧容量 -PermSize: 方法區大小 -MaxPermSize: 最大方法區大小 -MaxDirectMemorySize: 最大直接記憶體大小 二、java虛擬機器基本結構 1.
jvm記憶體分配和垃圾回收機制
問題: 1、垃圾回收目標物件? 2、什麼時間進行垃圾回收?(面試最常見的問題之一) 3、jvm怎樣進行垃圾回收? jvm記憶體分配 執行緒共享區域 1、 堆 2、方法區 執行緒私有區域 1、jvm棧 2、本地方法棧 3、程式計數器 由於虛擬機器棧,
JVM(2)不同的垃圾回收器的比較
4款Java垃圾回收器——錯誤的選擇導致糟糕的效能 1. -XX:+UseSerialGC 序列回收器 面向單執行緒環境的(比如說32位的或者Windows)以及比較小的堆。這個回收器工作的時候會將所有應用執行緒全部凍結,就這一點而言
JVM GC演算法和垃圾收集器
演算法: 標記-清除:產生大量離散碎片 複製演算法:三個區域eden(大)+2*survivor(小);每次在eden新建物件,gc的時候複製到另外一個survivor中,清除以前的兩個區域。 標記-整理:壓縮可用區域,將可用物件擠在一起 垃圾收集器:
JVM中的G1垃圾回收器
我們先回顧一下主流Java的垃圾回收器(HotSpot JVM)。本文是針對堆的垃圾回收展開討論的。 堆被分解為較小的三個部分。具體分為:新生代、老年代、持久代。 絕大部分新生成的物件都放在Eden區,當Eden區將滿,JVM會因申請不到記憶體,而觸發Young G
JVM記憶體分割槽和垃圾回收GC機制
JVM記憶體分割槽 JVM(Java virtual machine),即Java虛擬機器,它的厲害之處在於平臺無關性,“一處編寫,到處執行”。JVM通過執行目標位元組碼(.class),解釋在不同平臺上的機器執行,所以在具體的平臺上並不產生直接依賴。 JV
GC和垃圾回收器其一
什麼是GC GC(Garbage Collection)垃圾回收,釋放垃圾佔用的空間,對堆中已經死亡或者長時間沒有使用的物件進行清
JVM記憶體管理和垃圾回收
無論對於Java程式設計師還是大資料研發人員,JVM是必須掌握的技能之一。既是面試中經常問的問題,也是在實際業務中對程式進行調優、排查類似於記憶體溢位、棧溢位、記憶體洩漏等問題的關鍵。筆者將按下圖分多篇文章詳細闡述JVM: 本篇文章主要敘述JVM記憶體管理、直接記憶體、垃圾回收和常見的垃圾回
JVM調優總結(四)-分代垃圾回收詳述
web服務器 mar you 數量 不變 all 時間 lis 完成 為什麽要分代 分代的垃圾回收策略,是基於這樣一個事實:不同的對象的生命周期是不一樣的。因此,不同生命周期的對象可以采取不同的收集方式,以便提高回收效率。 在Java程序運行的過程中,會
JVM調優總結(三)-垃圾回收面臨的問題
也會 直接 問題 行程 完成 情況 出現 基本類型 不能 如何區分垃圾 上面說到的“引用計數”法,通過統計控制生成對象和刪除對象時的引用數來判斷。垃圾回收程序收集計數為0的對象即可。但是這種方法無法解決循環引用。所以,後來實現的垃圾判斷算法
JVM調優總結(二)-基本垃圾回收算法
會有 width 順序 系統 不知道 對待 循環引用 compact 垃圾回收算法 可以從不同的的角度去劃分垃圾回收算法: 按照基本回收策略分 引用計數(Reference Counting): 比較古老的回收算法。原理是此對象有一個引用,即增加一個計數,刪除一