JVM內(nèi)存區(qū)域以及各區(qū)域的內(nèi)存溢出異常瘾敢,內(nèi)存分代策略,垃圾收集算法尿这,各種垃圾收集器

本文整理自周志明老師的《深入理解Java虛擬機-JVM高級特性與最佳實踐》第3版的第二章和第三章簇抵。
加上了一些網(wǎng)上拼拼湊湊的圖片,個人認為很多博客復制來復制去射众,最后的東西都看不懂碟摆,所以從書里碼了一下知識點,也用作自己記憶叨橱。

一典蜕、一個命令

上面的結(jié)果顯示了 jvm 的模式:

Client VM(-client),為在客戶端環(huán)境中減少啟動時間而優(yōu)化罗洗;
Server VM(-server)愉舔,為在服務(wù)器環(huán)境中最大化程序執(zhí)行速度而設(shè)計。

在文件路徑:jdk-11.0.7+10\lib 下面可以更改 jvm.cfg 文件來決定是采用哪個模式伙菜,具體操作就是更改文件里面 Client 和 Server 這兩行的位置轩缤,誰在上就是選擇誰。

二、JVM 的內(nèi)存區(qū)域與內(nèi)存溢出異常

如上圖所示,是 Java 虛擬機規(guī)范規(guī)定的,jvm 管理的內(nèi)存區(qū)域涉馁。

  • 灰色部分币励,即方法區(qū)這兩個數(shù)據(jù)區(qū),是所有線程共享的數(shù)據(jù)區(qū)。
  • 而白色部分,包括程序計數(shù)器、java虛擬機棧勃救、本地方法棧,叫線程隔離的數(shù)據(jù)區(qū)脱茉,或者叫線程私有的內(nèi)存剪芥。這三塊內(nèi)存區(qū)域隨線程生,隨線程死琴许。

每個部分的詳細介紹如下:

2.1 pc 寄存器( Program Counter)

也可叫程序計數(shù)器税肪。是一塊較小的內(nèi)存空間,可以看作是當前線程執(zhí)行的字節(jié)碼的行號指示器榜田。

在虛擬機的概念模型(注意只是概念)里益兄,字節(jié)碼解釋器工作的時候就是通過改變這個計數(shù)器的值來選取嚇一跳需要執(zhí)行的字節(jié)碼指令,顯然箭券,分支循環(huán)等基礎(chǔ)功能都要靠這個計數(shù)器净捅。

由于多線程實際上是線程輪流切換實現(xiàn)的,所以線程切換后為了能恢復到正確的執(zhí)行位置辩块,每個線程都要有一個獨立的程序計數(shù)器蛔六。如果線程執(zhí)行的是一個 java 方法,計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址废亭;如果正在執(zhí)行的是本地方法国章,計數(shù)器的值則為空(Undefined)。

此內(nèi)存區(qū)域是唯一個在java虛擬機規(guī)范里沒有規(guī)定任何 OutOfMemoryError情況的區(qū)域豆村。

2.2 java 虛擬機棧

棧是方法執(zhí)行的線程內(nèi)存模型液兽。每個方法執(zhí)行的時候,jvm都會同步創(chuàng)建一個棧幀用于存儲局部變量表掌动、操作數(shù)棧等到四啰,方法被調(diào)用直到執(zhí)行完畢,就是對應(yīng)一個棧幀在虛擬機棧里從入棧到出棧的過程粗恢。

大多情況棧主要指的是虛擬機棧里局部變量表的部分(實際上的劃分要更復雜)柑晒。局部變量表存放了各種基本java數(shù)據(jù)類型、對象引用和 returnAddress 類型(指向了一條字節(jié)碼指令的地址)眷射。這些數(shù)據(jù)類型在局部變量表中以局部變量槽(Slot)來表示匙赞,其中64位長的long和double類型占用兩個槽恋追,其他的占一個。在編譯期間罚屋,局部變量表的空間就會分配完成,方法運行期間不會改變局部變量表的大小嗅绸。

java虛擬機規(guī)范對這個內(nèi)存區(qū)域規(guī)定了兩種異常:如果線程請求的棧深度大于虛擬機允許的深度脾猛,會拋出StackOverflowError;如果Java棧容量可以動態(tài)擴展鱼鸠,當擴展的時候無法申請到足夠的內(nèi)存會拋出OutOfMemoryError猛拴。

2.3 本地方法棧

本地方法棧和 java 虛擬機棧類似,區(qū)別只是虛擬機棧為虛擬機執(zhí)行 Java 方法蚀狰,本地方法棧是為虛擬機使用到的本地方法服務(wù)愉昆。

因此本地方法棧也會在棧深度溢出或者棧擴展失敗時分別拋出拋出 StackOverflowError 和 OutOfMemoryError 異常。

2.4 java 堆

java 堆在虛擬機啟動的時候建立麻蹋,它是 java 程序最主要的內(nèi)存工作區(qū)域跛溉。

java堆的唯一目的就是存放對象實例。在JVM所管理的內(nèi)存中扮授,堆區(qū)是最大的一塊芳室,堆區(qū)也是Java GC機制所管理的內(nèi)存區(qū)域。需要注意刹勃,java堆只是邏輯上的連續(xù)區(qū)域堪侯,物理上可以不連續(xù)。

提到垃圾回收的時候總會說堆的區(qū)域劃分荔仁,但是實際上java虛擬機規(guī)范沒有規(guī)定伍宦,所謂的劃分是各種虛擬機實現(xiàn)的風格決定的。這部分后面垃圾回收的時候還會講乏梁。

java堆可以固定大小次洼,也可以實現(xiàn)成可擴展,當前主流的虛擬機都是按照可擴展來實現(xiàn)掌呜,基于 -Xmx和-Xms參數(shù)來設(shè)定滓玖。

異常:如果堆內(nèi)存不夠,并且堆也無法擴展质蕉,拋出OutOfMemoryError势篡。

2.5 方法區(qū)

用來存儲已經(jīng)被虛擬機加載的類型信息、常量模暗、靜態(tài)變量禁悠、即時編譯器編譯后的代碼緩存等數(shù)據(jù)。

在java虛擬機里把他描述為堆的一個邏輯部分兑宇,但是又要和堆區(qū)分開碍侦,還有一個別名叫“非堆”。類加載子系統(tǒng)負責從文件系統(tǒng)或者網(wǎng)絡(luò)中加載 Class 信息( ClassLoader 就是這個區(qū)域下的組件),加載的類信息就存放于方法區(qū)瓷产。(可以看到站玄,這里保存的東西都是唯一份的東西)

關(guān)于垃圾回收的永久代,一般都是指的方法區(qū)濒旦,原因是當時的hotspot虛擬機設(shè)計團隊把垃圾收集器的分代設(shè)計擴展到了這里株旷,或者說使用永久代實現(xiàn)了方法區(qū),后來因為這種方法更容易內(nèi)存溢出尔邓,永久代的設(shè)計已經(jīng)被取消:到j(luò)dk8完全放棄永久代晾剖,使用本地內(nèi)存的中元空間來替代這部分的功能。

異常:無法滿足新的內(nèi)存分配需求梯嗽,拋出OutOfMemoryError齿尽。

  • 運行時常量池是方法區(qū)的一部分,用來存放編譯器生成的各種字面量和符號引用灯节,在類加載后這些內(nèi)容都會進入方法區(qū)的常量池循头。

既然是方法區(qū)的一部分,顯然是受到方法區(qū)內(nèi)存的限制炎疆,如果常量池無法再申請到內(nèi)存贷岸,會拋出拋出OutOfMemoryError。

2.6 直接內(nèi)存

直接內(nèi)存指的就已經(jīng)不屬于虛擬機運行時數(shù)據(jù)區(qū)域的部分了磷雇,java虛擬機規(guī)范也沒有定義這塊內(nèi)存偿警。

java在jdk1.4 后,引入了 **NIO **類唯笙,允許 java 程序通過native函數(shù)庫直接分配堆外的內(nèi)存螟蒸,然后通過java堆里的 DirectByteBuffer對象作為對這一塊內(nèi)存的引用進行操作,在某些場景中能夠提高性能崩掘,因為避免了 java 堆和 native 堆的數(shù)據(jù)來回復制七嫌。

異常:頻繁使用也可能導致拋出OutOfMemoryError。畢竟雖然沒有收到j(luò)ava堆的限制苞慢,可是還是會受到本機的內(nèi)存诵原、以及處理器尋址空間的限制

三、垃圾回收算法

3.1 概述

上面的內(nèi)存區(qū)域里挽放,線程獨有的三個區(qū)域绍赛,并不需要過多考慮回收問題,因為分配和回收比較確定辑畦。

Java堆和方法區(qū)這兩個區(qū)域則有著很顯著的不確定性:一個接口的多個實現(xiàn)類需要的內(nèi)存可能會不一樣吗蚌,一個方法所執(zhí)行的不同條件分支所需要的內(nèi)存也可能不一樣,只有處于運行期間纯出,我們才能知道程序究竟會創(chuàng)建哪些對象蚯妇,創(chuàng)建多少個對象敷燎,這部分內(nèi)存的分配和回收是動態(tài)的

對于方法區(qū)箩言,永久代的遺留問題關(guān)注比較多硬贯,最主要的垃圾回收算法還都是關(guān)注堆內(nèi)存。

垃圾收集器所關(guān)注的正是這部分內(nèi)存該如何管理陨收,我們討論的相關(guān)算法也是針對這部分內(nèi)存澄成。

從如何判定對象消亡的角度處罰,垃圾收集算法可以分為“引用計數(shù)式”(Reference Counting GC)和 “ 追蹤式”(Tracing GC)兩類畏吓。主流的 java 虛擬機都采用的第二種。所以下面講的算法都是這種模式下面的卫漫。

3.2 判斷對象是否需要回收

垃圾回收第一件事要做的就是菲饼,確定哪些對象死了,哪些活著列赎,死了的才要進行回收宏悦。對于判斷,一般有兩種算法包吝。

  1. 引用計數(shù)法(Reference Counting)

給對象添加一引用計數(shù)器饼煞,被引用一次計數(shù)器值就加 1;當引用失效時诗越,計數(shù)器值就減 1砖瞧;計數(shù)器為 0 時,對象就是不可能再被使用的嚷狞,簡單高效块促。

存在問題:無法解決對象之間相互循環(huán)引用的問題,要想采用這個算法床未,還需要很多的額外處理竭翠。

  1. 可達性分析算法

通過一系列的稱為 "GC Roots" 的對象作為起始節(jié)點集合,從這些節(jié)點開始薇搁,根據(jù)引用關(guān)系向下搜索斋扰,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到 GC Roots 沒有任何引用鏈相連時啃洋,則證明此對象是不可能再被使用的传货。

image

可達性分析算法是當前主流商用程序語言的內(nèi)存管理子系統(tǒng)采用的算法。

  1. 哪些對象可以作為 GC Roots 呢宏娄?

java 技術(shù)體系里损离,固定可作為 GC Roots 的對象包括以下幾種:

  • 在虛擬機棧中引用的對象。比如各個線程被調(diào)用的方法堆棧中使用到的參數(shù)绝编、局部變量僻澎、臨時變量等貌踏;
  • 方法區(qū)中類靜態(tài)屬性引用的對象,比如java類的引用類型靜態(tài)變量窟勃;
  • 方法區(qū)中常量引用的對象祖乳,比如字符串常量池里的引用;
  • 本地方法棧JNI(也就是通常說的本地方法)中引用的對象秉氧;
  • java虛擬機內(nèi)部的引用眷昆,比如基本數(shù)據(jù)類型對應(yīng)的 Class 對象,一些常駐的異常對象汁咏,和系統(tǒng)類加載器亚斋;
  • 所有被同步鎖(synchronized關(guān)鍵字)持有的對象;
  • 反應(yīng) java 虛擬機內(nèi)部情況的 JMXBean攘滩、JVMTI 中注冊的回調(diào)帅刊、本地代碼緩存等。

除了這些漂问,還會有一些臨時加入的對象赖瞒,共同構(gòu)成 GC Roots 集合。

  1. 方法區(qū)的垃圾回收

前面已經(jīng)說過蚤假,主要的收集區(qū)域是堆栏饮,而且方法區(qū)垃圾收集的性價比也比較低。比如在 hotspot 虛擬機采用了元空間來實現(xiàn)永久代磷仰,在這個區(qū)域沒有垃圾收集行為袍嬉。

如果要回收,方法區(qū)的垃圾收集主要回收兩部分內(nèi)容:廢棄的常量和不再使用的類型灶平。

回收廢棄常量與回收Java堆中的對象非常類似冬竟。都是基于判斷是否還有對象引用指向這個常量。常量池中其他類(接口)民逼、方法泵殴、字段的符號引用也與此類似。

而判斷類型的回收要滿足三個條件:

  1. 該類的所有實例已經(jīng)被回收拼苍,也就是堆中不再存在該類以及任何派生子類的實例笑诅;
  2. 加載該類的類加載器已經(jīng)被回收(這個條件很難達成);
  3. 該類對應(yīng)的 java.lang.Class 對象沒有在任何地方被引用疮鲫,無法在任何地方通過反射訪問該類的方法吆你。

3.3 分代收集理論

網(wǎng)上很多都說是分代收集算法,但是顯然這并不是具體的算法俊犯,更像一種策略妇多,選擇組合各種具體的算法,周志明老師的書上寫的是分代收集理論燕侠。

分代就是結(jié)合堆的區(qū)域劃分者祖,然后講回收對象根據(jù)年齡不同放到不同區(qū)域立莉,這樣的基礎(chǔ)上可以對某一區(qū)域單獨進行垃圾回收,當然七问,分代收集策略蜓耻、對應(yīng)的內(nèi)存分代,都有消亡的趨勢械巡。

分代至少會分為新生代和老年代兩個區(qū)域刹淌,新生代垃圾收集結(jié)束后,還存活的對象會逐步晉升到老年代存放讥耗,具體結(jié)合這一節(jié)的收集算法有勾,區(qū)域的劃分下一節(jié)會講解。

在內(nèi)存分出不同的區(qū)域后古程,對不同區(qū)域的回收也起了不同的名字:

  • Minor GC / Young GC(新生代收集):目標只是新生代區(qū)域的收集蔼卡;
  • Major GC / Old GC(老年代收集):目標只是老年代的垃圾收集;
  • Mix GC(混合收集):目標是收集整個新生代+部分老年代的垃圾收集籍琳。目前只有 G1 收集器有這種行為。

以上三個都叫 Partial GC贷祈,也就是部分收集趋急。還有一種收集:

  • Full GC(整堆收集):收集整個 java 堆和方法區(qū)的垃圾收集。

3.4 具體的垃圾回收算法

之前看網(wǎng)上有的說法講最早势誊、基本的垃圾回收算法是

引用計數(shù)(Reference Counting):有一個引用就加一個技術(shù)呜达,少一個引用就減一個計數(shù),垃圾回收的時候就收集計數(shù)為 0 的粟耻。

但是現(xiàn)在我明白了查近,這玩意確實劃分的有點亂,引用計數(shù)正如 3.2 講到的挤忙,應(yīng)該算到 如何判斷垃圾是否需要回收的算法里霜威,不應(yīng)該算在垃圾回收算法里。

所以仍然按照深入理解java虛擬機書里講的册烈,分為三個算法戈泼。

3.4.1 標記-清除算法(Mark-Sweep)

掃描GC Roots集合:

  • 第一階段,從引用根節(jié)點赏僧,開始標記所有被引用的對象大猛;
  • 第二階段,遍歷整個堆淀零,把未標記的對象清除挽绩。
  • 也可以反過來,標記未被引用的對象驾中,然后清除未被標記的唉堪。
image

缺點

  • 效率很不穩(wěn)定模聋,如果堆包含大量對象,而大部分都要被收集巨坊,那么這個操作過程執(zhí)行效率一直降低撬槽;
  • 內(nèi)存空間碎片化,如上圖可以看的很明顯趾撵,垃圾收集執(zhí)行完后空間碎片過多侄柔,可能會導致以后程序運行的時候需要分配大對象的時候找不到連續(xù)內(nèi)存從而又提前觸發(fā)垃圾收集。

3.4.2 標記-復制算法(簡稱復制算法)

把內(nèi)存劃分為兩個相等的區(qū)域占调,每次只用一個區(qū)域暂题,一個內(nèi)存用完就開始執(zhí)行算法:

  • 把這個區(qū)域仍然存活的對象復制到另一個區(qū)域(這一步顯然還是要先標記的);
  • 然后把這個區(qū)域一次清理(省掉了上一種方法的第二次遍歷)究珊。
image

優(yōu)點

簡單薪者、高效,而且解決了產(chǎn)生空間碎片的問題剿涮。

缺點

需要 2 倍內(nèi)存言津,總是有一半空的,可用的也只有一半取试,太浪費了悬槽。

3.4.3 標記-整理(Mark-Compact)

結(jié)合標記清除算法的第一步,第二步并不采用直接清理瞬浓,而是讓所有對象都向內(nèi)存空間的固定一端挪動初婆,最后清理掉邊界之外的內(nèi)存。

image

優(yōu)點

顯然猿棉,進行垃圾收集后不會產(chǎn)生碎片磅叛。

缺點

“整理”的過程,或者說移動萨赁,如果是在老年代弊琴,每次都沉積著大量對象,移動的過程顯然會是一個很負重的操作杖爽,必須全程暫停用戶應(yīng)用程序访雪。這種停頓還被設(shè)計者描述為 Stop the world。

權(quán)衡:

  • 如果移動掂林,那么缺點已經(jīng)說過了臣缀;
  • 如果不移動,那么要通過更復雜的策略解決內(nèi)存碎片問題泻帮,而內(nèi)存的訪問本身又是用戶程序最頻繁的操作精置,額外的負擔會影響應(yīng)用程序的吞吐量。

也就是說锣杂,如果移動脂倦,內(nèi)存回收會更復雜番宁,如果不移動,內(nèi)存分配會更復雜赖阻。從整個程序的吞吐量來看蝶押,移動會更劃算。

注意:因為有標記的過程火欧,通常都是需要停頓用戶線程來進行的棋电,只是總體來說,最后一種有整理的過程苇侵,前兩種的停頓時間就會短一些赶盔。

四、JVM堆內(nèi)存分代策略

需要再次強調(diào)的是:

從回收內(nèi)存的角度看榆浓,由于現(xiàn)代垃圾收集器大部分都是基于上一節(jié)所說的于未,分代收集理論設(shè)計的,區(qū)域劃分僅僅是一部分垃圾收集器的共同特性或者說設(shè)計風格而已,而非某個Java虛擬機具體實現(xiàn)的固有內(nèi)存布局,更不是《Java虛擬機規(guī)范》里對Java堆的進一步細致劃分陡鹃。

尤其到 G1 收集器的出現(xiàn)后烘浦,已經(jīng)打破了固有的策略,往后萍鲸,垃圾收集器技術(shù)的更新也會帶來更多的策略闷叉,而不是分代。

因此我們從分水嶺的前后來分別介紹猿推。

內(nèi)存分代策略:也就是根據(jù)對象存活的周期不同片习,將堆內(nèi)存劃分為幾塊捌肴,一般分為新生代蹬叭、老年代、永久代状知。

4.1 為什么要分代秽五?

很好理解,因為各種對象示例需要回收的頻率是不一樣的饥悴,分區(qū)操作能縮小操作的范圍坦喘,結(jié)合上一節(jié)的垃圾回收策略,更好理解西设。

  • 如果沒有區(qū)域劃分瓣铣,頻繁進行垃圾收集的時候,遍歷范圍都是所有的對象贷揽,會嚴重影響的 GC 效率棠笑。
  • 有了內(nèi)存分代,根據(jù)不同區(qū)域禽绪,采用不同的垃圾收集算法蓖救。

4.2 內(nèi)存劃分具體策略

新生代洪规、老年代、永久代(如上一節(jié)所介紹的循捺,永久代后來已經(jīng)被取締)斩例。

image

4.2.1 新生代(Young)

新生代又分為了三塊區(qū)域,他們的空間比例默認為 8:1:1从橘。

  • Eden(伊甸園念赶,人類創(chuàng)建的地方),就是所有對象產(chǎn)生的地方洋满;
  • From晶乔,屬于第一塊 Survivor 區(qū)域;
  • To牺勾,屬于第二塊 Survivor 區(qū)域正罢。

這么個比例劃分是因為新生代的垃圾回收算法是標記-復制算法,設(shè)置這個比例是為了充分利用內(nèi)存空間驻民。

新生對象在 Eden 區(qū)分配翻具,除了大對象,大對象直接進入老年代回还。

大對象就是指需要大量連續(xù)內(nèi)存的對象裆泳,就是很大的數(shù)組,或者很長的字符串柠硕。比大對象更糟糕的就是遇到一個朝生夕滅的大對象工禾。

結(jié)合一般在這個區(qū)域采用標記-復制算法,看一看新生代的垃圾收集過程:

  • 如果 Eden 區(qū)不夠了蝗柔,就會開始一次 Minor GC闻葵,將 Eden 里存活的復制到 From(Eden空了);
  • 下次 Eden 區(qū)滿了癣丧,再執(zhí)行一次 Minor GC槽畔,將存活的對象復制到 To 中 (Eden空了),同時胁编,將 From 中消亡的對象清理掉厢钧,將存活的對象也復制到 To 區(qū),然后清空 From 區(qū)(此時 From空)嬉橙;

在 From 和 To 兩個區(qū)域的這種切換早直,顯然就是標記復制的算法,他們兩個的空間也確實是 1 : 1市框。此后從 Eden 區(qū)滿了后再往他們兩個區(qū)域移動的時候就是交替進行霞扬。

注意事項

  • 當兩個存活區(qū)切換了幾次(HotSpot虛擬機默認15次)之后,仍然存活的對象,將被復制到老年代祥得。實現(xiàn)方式兔沃,就是在不斷的 Minor GC ,這個復制的過程會給對象計算年齡级及,年齡計數(shù)器是存儲在對象頭里的(關(guān)于虛擬機的對象頭信息)乒疏。
  • 除了年齡判斷,hotspot 虛擬機還有動態(tài)對象年齡判定的策略饮焦,如果 survivor 空間相同年齡所有對象大小總和 >= Survivor 空間的一半怕吴,這部分對象都直接進入老年代。

所以可以總結(jié)出有 3 類對象都會進入老年代:1.大對象直接進县踢;2.在Minor GC 存活15歲后進转绷;3.相同年齡對象成為眾數(shù),一起進硼啤。

4.2.2 老年代(Old)

這里的對象GC 頻率低议经。

4.2.3 永久代(Permanent)

正如前面所說,jdk8以前谴返,很多人愿意把方法區(qū)稱為永久代煞肾,本質(zhì)上是因為當時的hotspot虛擬機選擇把垃圾收集的設(shè)計擴展到了方法區(qū),或者說使用永久代實現(xiàn)方法區(qū)嗓袱,使得垃圾收集器能夠管理這部分內(nèi)存籍救,其他虛擬機不存在這個概念。

到j(luò)dk8就完全放棄了渠抹,因為實現(xiàn)方法區(qū)的內(nèi)容已經(jīng)改為用本地內(nèi)存的元空間蝙昙。

這里其實我有一個疑問,邏輯上本來方法區(qū)是屬于堆的一塊特殊區(qū)域梧却,現(xiàn)在改用本地直接內(nèi)存來實現(xiàn)奇颠,那么在內(nèi)存區(qū)域的劃分上,是應(yīng)該定義為直接內(nèi)存的一塊特殊區(qū)域篮幢?

反正說 jvm 的內(nèi)存區(qū)域的時候迷迷糊糊的大刊。

五为迈、垃圾回收器

這里指的都是“經(jīng)典”垃圾回收器三椿,是因為目前的新技術(shù)實現(xiàn)的高性能低延遲收集器還處于實驗狀態(tài)。

所以記錄一下時間:現(xiàn)在是2020.09.04葫辐,參考的書是基于 jdk11 的搜锰。

5.1 Serial 收集器(復制算法)

新生代單線程收集器,標記和清理都是單線程耿战,需要其它工作線程暫停蛋叼,優(yōu)點是簡單高效。

這也是虛擬機在Client模式下運行的默認值,可以通過 -XX:+UseSerialGC 來強制指定狈涮。

5.2 Serial Old 收集器(標記-整理算法)

老年代單線程收集器狐胎,Serial收集器的老年代版本,需要其它工作線程暫停歌馍,簡單高效握巢。

5.3 ParNew 收集器(復制算法)

新生代收集器,實質(zhì)上是 Serial 收集器的多線程版本松却,各種策略都和 Serial 收集器一樣暴浦。除了支持多線程并行,沒有別的優(yōu)點晓锻,但是在 jdk7 之前歌焦,都會用他,原因和性能無關(guān)砚哆,原因是:只有他能和 CMS 配合工作独撇。(之后有 G1 了,他就沒這么高地位了)

5.4 Parallel Scavenge 收集器(復制算法)

新生代收集器躁锁,并行券勺,表面上看起來的特性和 ParNew 一樣。

但是他的特點是灿里,關(guān)注點不在縮短線程停頓時間关炼,而關(guān)注如何達到一個可控制的吞吐量,什么是吞吐量匣吊?

image

Parallel Scavenge+Serial Old 收集器組合回收垃圾(這也是在Server模式下的默認值)可用 -XX:+UseParallelGC 來強制指定儒拂,用 -XX:ParallelGCThreads=4 來指定線程數(shù)。

5.5 Parallel Old 收集器(標記-整理算法)

Parallel Scavenge 收集器的老年代版本色鸳,并行收集器社痛。

Parallel Scavenge 和 Parallel Old 搭配,產(chǎn)生了一種“吞吐量”優(yōu)先的收集器方案命雀。

5.6 CMS(Concurrent Mark Sweep)收集器(標記-清除算法)

老年代收集器蒜哀。從名字就可以看出來,是并發(fā)+標記清除吏砂。一些官方公開文檔里害稱之為Concurrent Low Pause Collector撵儿,并發(fā)低停頓收集器。

他的收集過程比較復雜狐血,分為四步:

  1. 初始標記淀歇;(需要停頓用戶線程,標記GC roots能直接關(guān)聯(lián)到的對象匈织,快)
  2. 并發(fā)標記浪默;(從上一步關(guān)聯(lián)到的對象遍歷整個圖牡直,但是是并發(fā)運行的,不用停頓用戶線程纳决,慢)
  3. 重新標記碰逸;(修正上一個階段可能因為用戶繼續(xù)操作又產(chǎn)生變動的部分,需要停頓用戶線程阔加,快)
  4. 并發(fā)清除花竞。(并發(fā)執(zhí)行,因為不需要整理移動存活對象)

最大的優(yōu)點就是名字體現(xiàn)出來的:并發(fā)收集掸哑、低停頓约急。

缺點

  • 對處理器資源非常敏感,原因就是苗分,雖然你是并發(fā)的厌蔽,但是你本身相當于其他的線程,這是境地總吞吐量的(空間換時間嘛)摔癣;
  • 無法處理浮動垃圾奴饮。浮動垃圾就是說,他的四個步驟里择浊,并發(fā)的兩個步驟戴卜,用戶線程都是在同時產(chǎn)生垃圾的,只能等到下一次才能處理琢岩。所以垃圾收集還需要有額外預(yù)留的空間投剥,否則還會產(chǎn)生問題;
  • 因為是標記清除算法担孔,所以有空間碎片以及后續(xù)會產(chǎn)生的問題江锨。

5.7 G1/Garbage First 收集器

這是一個里程碑式的成果。實驗期完成后糕篇,正式商用啄育,到j(luò)dk8后,官方稱之為全功能垃圾收集器(Fullly-Featured Garbage Collector)拌消。

jdk 9 后挑豌,G1 也替代了Parallel Scavenge 和 Parallel Old 搭配的組合,稱為服務(wù)端模式下的默認收集器墩崩,CMS 直接淪落到了不推薦使用氓英。

之前垃圾收集的目標都是基于分代的內(nèi)存,要么在新生代工作泰鸡、要么老年代债蓝、要么整個 java 堆壳鹤。G1 則跳出了這個牢籠盛龄,可以面向堆內(nèi)存的任何部分來組成回收集(Collection Set,簡稱 CSet),衡量標準不再是哪個分代余舶,而是哪塊垃圾最多我去哪快啊鸭,這就是 G1 收集器的 Mixed GC 模式。

G1 把堆內(nèi)存分為了不同的 Region 匿值,這些 Region 大小相等赠制,各自獨立。這個劃分不像以前遵循的那種固定比例挟憔,這樣钟些,每個 Region 都可能扮演以前的新生代的 Eden 空間、Survivor空間或者老年代空間绊谭,然后垃圾收集器采用不同的策略去收集政恍。

缺點:比CMS有更高的內(nèi)存占用炒瘸,更高的額外執(zhí)行負載定踱。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市引润,隨后出現(xiàn)的幾起案子宪赶,更是在濱河造成了極大的恐慌宗弯,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搂妻,死亡現(xiàn)場離奇詭異蒙保,居然都是意外死亡,警方通過查閱死者的電腦和手機欲主,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門追他,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人岛蚤,你說我怎么就攤上這事邑狸。” “怎么了涤妒?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵单雾,是天一觀的道長。 經(jīng)常有香客問我她紫,道長硅堆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任贿讹,我火速辦了婚禮渐逃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘民褂。我一直安慰自己茄菊,他們只是感情好疯潭,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著面殖,像睡著了一般竖哩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上脊僚,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天相叁,我揣著相機與錄音,去河邊找鬼辽幌。 笑死增淹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的乌企。 我是一名探鬼主播埠通,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼逛犹!你這毒婦竟也來了端辱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤虽画,失蹤者是張志新(化名)和其女友劉穎舞蔽,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體码撰,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡渗柿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脖岛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朵栖。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖柴梆,靈堂內(nèi)的尸體忽然破棺而出陨溅,到底是詐尸還是另有隱情,我是刑警寧澤绍在,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布门扇,位于F島的核電站,受9級特大地震影響偿渡,放射性物質(zhì)發(fā)生泄漏臼寄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一溜宽、第九天 我趴在偏房一處隱蔽的房頂上張望吉拳。 院中可真熱鬧,春花似錦适揉、人聲如沸留攒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽稼跳。三九已至盟庞,卻和暖如春吃沪,著一層夾襖步出監(jiān)牢的瞬間汤善,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工票彪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留红淡,地道東北人。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓降铸,卻偏偏與公主長得像在旱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子推掸,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容