一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - JVM垃圾收集器詳解

JVM垃圾收集器詳解

2020-08-11 18:57LSPZ Java教程

本文主要介紹了JVM垃圾收集器的相關知識。具有很好的參考價值,下面跟著小編一起來看下吧

說起垃圾收集(garbage collection,gc),大部分人都把這項技術當做java語言的伴生產物。事實上,gc的歷史遠比java久遠,1960年誕生于mit的lisp是第一門真正使用內存動態分配和垃圾收集技術的語言。當list還在胚胎時期時,人們就在思考gc需要完成的3件事情:

  1. 哪些內存需要回收?
  2. 什么時候回收?
  3. 如何回收?

一、哪些內存需要回收?

從jvm區域結構看,可將這些區域劃分為“靜態內存”和“動態內存”兩類。程序計數器、虛擬機棧、本地方法3個區域是“靜態”的,因為這幾個區域的內存分配和回收都具備確定性,都隨著線程而生,隨著線程而滅。但java堆和方法區不一樣,內存分配都存在不確定性,只有在程序處于運行期間才能知道會創建哪些對象,這部分內存和回收都是動態的,垃圾收集器所關注的是這部分內存。

在堆里面存放著java世界幾乎所有的對象實例,垃圾回收器在對堆進行回收前,第一件事情就是就是要確定這些對象哪些還"存活"著,哪些已經"死去"。那么又怎么確定對象已經"死去"呢?

1.引用計數法:

分配對象時給對象添加一個引用計數器,每當有一個地方引用它時,計數器值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的對象就是沒有再被使用了。客觀地說,引用計數法(reference counting)的實現簡單,判斷效率也很高,但是在主流的java虛擬機里面沒有選用引用計數法來管理內存,其中最主要的原因是它很難解決對象之間相互循環引用的問題。例如:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class referencecountinggc {
 public object instance = null;
 private byte[] bigsize = new byte[2*1024*1024];
 public static void testgc(){
 referencecountinggc obja = new referencecountinggc();
 referencecountinggc objb = new referencecountinggc();
 obja.instance = objb;
 objb.instance = obja;
 obja = null;
 objb = null;
 system.gc();
 }
}

當設置obja = null;objb = null后這兩個對象再無任何引用,實際上這兩個對象已經不可能再被訪問,但是它們因為互相引用著對方,導致它們的引用計數都不為0,于是引用計數算法無法通知gc收集器回收它們。如果這個對象特別大,則會造成嚴重的內存泄露。

2.可達性分析算法:

可達性分析(reachability analysis)的基本思想是通過一系列的稱為“gc roots”的對象作為起始點,從這些節點開始向下搜索,搜索所走過的路徑稱為引用鏈(reference chain),當一個對象到gc roots沒有任何引用鏈相連時(也就是gc roots到這個對象不可達),則證明此對象是不可用的。如下圖所示:

 JVM垃圾收集器詳解

對象object5、object6、object7相互雖然有關聯,但是它們到gc roots是不可達的,所以它們將會被判定為是可回收的對象。在java語言中,可作為gc roots的對象包括下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象。
  • 方法區中類靜態屬性引用的對象。
  • 方法區中常量引用的對象。
  • 本地方法棧中jni(即一般說的native方法)引用的對象。

二、什么時候回收?

虛擬機為了分析gc roots這項工作必須在一個能確保一致性的快照中進行,這里的“一致性”的意思就是指在整個分析期間整個執行系統看起來就像被凍結在某個時間點上——這叫安全點。當然,程序執行時并非在所有地方都能停頓下來開始gc,只有到達安全點時才能暫停。安全點選址也有規定的,選定基本上是以程序“是否具有讓程序長時間執行的特征”為標準進行選定的。這里的長時間執行的最明顯特征是指令列復用,例如方法調用、循環跳轉、異常跳轉等。

虛擬機為了能讓所有線程都“跑”到安全點上停頓下來,設計了兩個方案:搶先式中斷和主動式中斷。其中搶先式中斷是虛擬機發生gc時,首先把所有線程全部中斷,如果發生有線程中斷的地方不在安全點上,就恢復線程,讓它“跑”到安全點上。這種方式現在比較用了。而主動式中斷是虛擬機需要gc時僅僅簡單的設置一個標志,各個線程執行到安全點時主動去輪詢這個標志,發現中斷標志為真時就自己中斷掛起。

三、如何回收?

3.1 垃圾收集算法:

(1)標記-清除(mark-sweep)算法

這是最基礎的算法,就像它名字一樣,算法分為“標記”和“清除”兩個階段:首先標記處所有需要回收的對象(如哪些內存需要回收所描述的對象),對標記完成后統一回收所有被標記的對象,如下圖所示:

JVM垃圾收集器詳解

缺點:一個是效率問題,標記和清除兩個過程的效率都不高;另一個是空間問題,標記清除后悔產生大量的不連續的內存碎片,可能會導致后續無法分配大對象而導致再一次觸發垃圾收集動作。

(2)復制算法

為了針對標記-清除算法的不足,復制算法將可用內存容量劃分為大小相等的兩塊,每次只使用一塊。當一塊的內存用完了,就將還存活的對象復制到另一塊上面去。然后把已使用過的內存空間一次清理掉,如下圖所示:

JVM垃圾收集器詳解

缺點:使用內存比原來縮小了一半。

現在的商業虛擬機都采用這種收集算法來回收新生代,有企業分析的得出其實并不需求將內存按1:1的比例劃分,因為新生代中的對象大部分都是“朝生夕死”的。所以,hotspot虛擬機默認的eden和survivor的大小比例是8:1。一塊eden和兩塊survivor,每次使用一塊eden和一塊survivor,也就是說只有10%是浪費的。如果另一塊survivor都無法存放上次垃圾回收的對象時,那這些對象將通過“擔保機制”進入老年代了。

(3)標記-整理(mark-compact)算法

復制算法一般是對對象存活率較低的一種回收操作,但對于對象存活率較高的內存區域(老年代)來說,效果就不是那么理想了,標記-整理算法因此誕生了。標記-整理算法和標記-清除算法差不多,都是一開始對回收對象進行標記,但后續不是直接對對象清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存,如下圖所示:

JVM垃圾收集器詳解

(4)分代收集算法

分代收集算法是目前大部分jvm的垃圾收集器采用的算法。它的核心思想是根據對象存活的生命周期將內存劃分為若干個不同的區域。一般情況下將堆區劃分為老年代(tenured generation)和新生代(young generation),老年代的特點是每次垃圾收集時只有少量對象需要被回收,而新生代的特點是每次垃圾回收時都有大量的對象需要被回收,那么就可以根據不同代的特點采取最適合的收集算法。

3.2 垃圾收集器:

(1)七種垃圾收集器:

  1. serial(串行gc)-復制
  2. parnew(并行gc)-復制
  3. parallel scavenge(并行回收gc)-復制
  4. serial old(msc)(串行gc)-標記-整理
  5. cms(并發gc)-標記-清除
  6. parallel old(并行gc)--標記-整理
  7. g1(jdk1.7update14才可以正式商用)

說明:

  1. 1~3用于年輕代垃圾回收:年輕代的垃圾回收稱為minor gc
  2. 4~6用于年老代垃圾回收(當然也可以用于方法區的回收):年老代的垃圾回收稱為full gc
  3. g1獨立完成"分代垃圾回收"

注意:并行與并發

  1. 并行:多條垃圾回收線程同時操作
  2. 并發:垃圾回收線程與用戶線程一起操作

(2)常用五種組合:

  1. serial/serial old
  2. parnew/serial old:與上邊相比,只是比年輕代多了多線程垃圾回收而已
  3. parnew/cms:當下比較高效的組合
  4. parallel scavenge/parallel old:自動管理的組合
  5. g1:最先進的收集器,但是需要jdk1.7update14以上

(2.1)serial/serial old:

 JVM垃圾收集器詳解

特點:

  • 年輕代serial收集器采用單個gc線程實現"復制"算法(包括掃描、復制)
  • 年老代serial old收集器采用單個gc線程實現"標記-整理"算法
  • serial與serial old都會暫停所有用戶線程(即stw)

說明:

stw(stop the world):編譯代碼時為每一個方法注入safepoint(方法中循環結束的點、方法執行結束的點),在暫停應用時,需要等待所有的用戶線程進入safepoint,之后暫停所有線程,然后進行垃圾回收。

適用場合:

  • cpu核數<2,物理內存<2g的機器(簡單來講,單cpu,新生代空間較小且對stw時間要求不高的情況下使用)
  • -xx:useserialgc:強制使用該gc組合
  • -xx:printgcapplicationstoppedtime:查看stw時間
  • 由于它實現相對簡單,沒有線程相關的額外開銷(主要指線程切換與同步),因此非常適合運行于客戶端pc的小型應用程序,或者桌面應用程序(比如swing編寫的用戶界面程序),以及我們平時的開發、調試、測試等。

(2.2)parnew/serial old:

 JVM垃圾收集器詳解

說明:

parnew除了采用多gc線程來實現復制算法以外,其他都與serial一樣,但是此組合中的serial old又是一個單gc線程,所以該組合是一個比較尷尬的組合,在單cpu情況下沒有serial/serial old速度快(因為parnew多線程需要切換),在多cpu情況下又沒有之后的三種組合快(因為serial old是單gc線程),所以使用其實不多。

-xx:parallelgcthreads:指定parnew gc線程的數量,默認與cpu核數相同,該參數在于cms gc組合時,也可能會用到

(2.3)parallel scavenge/parallel old:

 JVM垃圾收集器詳解

特點:

  1. 年輕代parallel scavenge收集器采用多個gc線程實現"復制"算法(包括掃描、復制)
  2. 年老代parallel old收集器采用多個gc線程實現"標記-整理"算法
  3. parallel scavenge與parallel old都會暫停所有用戶線程(即stw)

說明:

  1. 吞吐量:cpu運行代碼時間/(cpu運行代碼時間+gc時間)
  2. cms主要注重stw的縮短(該時間越短,用戶體驗越好,所以主要用于處理很多的交互任務的情況)
  3. parallel scavenge/parallel old主要注重吞吐量(吞吐量越大,說明cpu利用率越高,所以主要用于處理很多的cpu計算任務而用戶交互任務較少的情況)

參數設置:

  1. -xx:+useparalleloldgc:使用該gc組合
  2. -xx:gctimeratio:直接設置吞吐量大小,假設設為19,則允許的最大gc時間占總時間的1/(1 +19),默認值為99,即1/(1+99)
  3. -xx:maxgcpausemillis:最大gc停頓時間,該參數并非越小越好
  4. -xx:+useadaptivesizepolicy:開啟該參數,-xmn/-xx:survivorratio/-xx:pretenuresizethreshold這些參數就不起作用了,虛擬機會自動收集監控信息,動態調整這些參數以提供最合適的的停頓時間或者最大的吞吐量(gc自適應調節策略),而我們需要設置的就是-xmx,-xx:+useparalleloldgc或-xx:gctimeratio兩個參數就好(當然-xms也指定上與-xmx相同就好)

適用場合:

  1. 很多的cpu計算任務而用戶交互任務較少的情況
  2. 不想自己去過多的關注gc參數,想讓虛擬機自己進行調優工作
  3. 對吞吐量要求較高,或需要達到一定的量。

(2.4)parnew/cms:

 JVM垃圾收集器詳解

說明:

  1. 以上只是年老代cms收集的過程,年輕代parnew看"2.2、parnew/serial old"就好
  2. cms是多回收線程的,不要被上圖誤導,默認的線程數:(cpu數量+3)/4
  3. cms主要注重stw的縮短(該時間越短,用戶體驗越好,所以主要用于處理很多的交互任務的情況)

特點:

1.年輕代parnew收集器采用多個gc線程實現"復制"算法(包括掃描、復制)

2.年老代cms收集器采用多線程實現"標記-清除"算法

  • 初始標記:標記與根集合節點直接關聯的節點。時間非常短,需要stw
  • 并發標記:遍歷之前標記到的關聯節點,繼續向下標記所有存活節點。時間較長。
  • 重新標記:重新遍歷trace并發期間修改過的引用關系對象。時間介于初始標記與并發標記之間,通常不會很長。需要stw
  • 并發清理:直接清除非存活對象,清理之后,將該線程占用的cpu切換給用戶線程

3.初始標記與重新標記都會暫停所有用戶線程(即stw),但是時間較短;并發標記與并發清理時間較長,但是不需要stw

關于并發標記期間怎樣記錄發生變動的引用關系對象,在重新標記期間怎樣掃描這些對象

缺點:

  • 并發標記與并發清理:按照說明的第二點來講,假設有2個cpu,那么其中有一個cpu會用于垃圾回收,而另一個用于用戶線程,這樣的話,之前是兩cpu運行用戶線程,現在是一個,那么效率就會急劇下降。也就是說,降低了吞吐量(即降低了cpu使用率)。
  • 并發清理:在這一過程中,產生的垃圾無法被清理(因為發生在重新標記之后)
  • 并發標記與并發清理:由于是與用戶線程并發的,所以用戶線程可能會分配對象,這樣既可能對象直接進入年老代(例如,大對象),也可能進入年輕代后,年輕代發生minor gc,這樣的話,實際上要求我們的年老代需要預留一定空間,也就是說要在年老代還有一定空間的情況下就要進行垃圾回收,留出一定內存空間來供其他線程使用,而不能等到年老代快爆滿了才進行垃圾回收,通過-xx:cmsinitiatingoccupancyfraction來指定當年老代空間滿了多少后進行垃圾回收
  • 標記-清理算法:會產生內存碎片,由于是在老年代,可能會提前觸發full gc(這正是我們要盡量減少的)

參數設置:

  • -xx:+useconcmarksweepgc:使用該gc組合
  • -xx:cmsinitiatingoccupancyfraction:指定當年老代空間滿了多少后進行垃圾回收
  • -xx:+usecmscompactatfullcollection:(默認是開啟的)在cms收集器頂不住要進行fullgc時開啟內存碎片整理過程,該過程需要stw
  • -xx:cmsfullgcsbeforecompaction:指定多少次fullgc后才進行整理
  • -xx:parallelcmsthreads:指定cms回收線程的數量,默認為:(cpu數量+3)/4

適用場合:

用于處理很多的交互任務的情況

方法區的回收一般使用cms,配置兩個參數:-xx:+cmspermgensweepingenabled與-xx:+cmsclassunloadingenabled

適用于一些需要長期運行且對相應時間有一定要求的后臺程序

(2.5)g1

 JVM垃圾收集器詳解

說明:

  • 從上圖來看,g1與cms相比,僅在最后的"篩選回收"部分不同(cms是并發清除),實際上g1回收器的整個堆內存的劃分都與其他收集器不同。
  • cms需要配合parnew,g1可單獨回收整個空間

原理:

  • g1收集器將整個堆劃分為多個大小相等的region
  • g1跟蹤各個region里面的垃圾堆積的價值(回收后所獲得的空間大小以及回收所需時間長短的經驗值),在后臺維護一張優先列表,每次根據允許的收集時間,優先回收價值最大的region,這種思路:在指定的時間內,掃描部分最有價值的region(而不是掃描整個堆內存),并回收,做到盡可能的在有限的時間內獲取盡可能高的收集效率。

運作流程:

  • 初始標記:標記出所有與根節點直接關聯引用對象。需要stw
  • 并發標記:遍歷之前標記到的關聯節點,繼續向下標記所有存活節點。在此期間所有變化引用關系的對象,都會被記錄在remember set logs中
  • 最終標記:標記在并發標記期間,新產生的垃圾。需要stw
  • 篩選回收:根據用戶指定的期望回收時間回收價值較大的對象(看"原理"第二條)。需要stw

優點:

  1. 停頓時間可以預測:我們指定時間,在指定時間內只回收部分價值最大的空間,而cms需要掃描整個年老代,無法預測停頓時間
  2. 無內存碎片:垃圾回收后會整合空間,cms采用"標記-清理"算法,存在內存碎片
  3. 篩選回收階段:
  • 由于只回收部分region,所以stw時間我們可控,所以不需要與用戶線程并發爭搶cpu資源,而cms并發清理需要占據一部分的cpu,會降低吞吐量。
  • 由于stw,所以不會產生"浮動垃圾"(即cms在并發清理階段產生的無法回收的垃圾)

適用范圍:

  • 追求stw短:若parnew/cms用的挺好,就用這個;若不符合,用g1
  • 追求吞吐量:用parallel scavenge/parallel old,而g1在吞吐量方面沒有優勢

以上就是本文的全部內容,希望本文的內容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持服務器之家!

原文鏈接:http://www.cnblogs.com/lspz/p/6397649.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 公翁的舌尖研磨她的花蒂小说 | 亚洲波多野结衣日韩在线 | 欧美黑人成人免费全部 | 无人区国产大片 | 成年人免费在线看 | 公交车强校花系列小说 | 国产精品亚欧美一区二区三区 | 精品小视频在线 | 天天操天天爽天天射 | 国产午夜成人无码免费看 | 国产色视频一区二区三区 | 俄罗斯妈妈235 | 亚洲国产福利精品一区二区 | 狠狠澡| 果冻传媒第一二三专区 | 99亚洲 | 嫩草在线视频www免费观看 | 国产欧美日韩高清专区ho | 精品国产福利在线 | 久久久久久免费观看 | 美女污视频在线观看 | 女人张开腿让男人桶视频免费大全 | 99在线免费播放 | 啊好大好爽 | 毛片在线网址 | 末代皇帝无删减版在线观看 | 娇小8一12xxxx第一次 | 亚洲午夜精品久久久久久成年 | 亚洲欧美天堂 | 青草热久精品视频在线观看 | 亚洲精品αv一区二区三区 亚洲精品91大神在线观看 | 国产日韩精品一区二区在线观看播放 | 黑人好大 | 无遮挡h肉动漫高清在线 | 被老头肉至怀孕小说 | 美女mm131爽爽爽久久 | 国产成人精品一区二区阿娇陈冠希 | 午夜在线观看免费完整直播网页 | 国产欧美一区二区三区免费看 | 国产福利不卡视频 | 国产精自产拍久久久久久 |