自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

GC詳解,看完這篇同事小勇都驚呆了

開(kāi)發(fā) 后端
GC是一種自動(dòng)的存儲(chǔ)管理機(jī)制。當(dāng)一些被占用的內(nèi)存不再需要時(shí),就應(yīng)該予以釋放。這種存儲(chǔ)資源管理,稱為垃圾回收。

[[402149]]

前言

在一個(gè)風(fēng)和日麗的中午,和同事小勇一起走在公司樓下的小公園里面,看到很多的小姐姐,心想什么時(shí)候能夠和這些小姐姐一起討論人生呀,美滋滋,嘿嘿嘿。

  • 收起你的哈喇子好不好,小勇總是在這個(gè)時(shí)候發(fā)出聲音,挺讓人喜(fu)歡(ck)的。
  • 小勇:小農(nóng),現(xiàn)在不是推崇垃圾分類嗎,你說(shuō)到底什么是垃圾?小勇總是在我和他散步的時(shí)候,問(wèn)這么讓人深思的問(wèn)題!
  • 我:什么是垃圾啊,你不就是垃圾嗎?
  • 小勇:去你大爺?shù)?,正?jīng)的。
  • 我:小勇啊,答應(yīng)我以后散步的時(shí)候我們討論點(diǎn)輕松點(diǎn)的問(wèn)題好嘛?垃圾是啥,垃圾就是沒(méi)有引用的對(duì)象就是垃圾啊
  • 小勇:。。。。,我們還是去午休吧
  • 我:別啊,都講到這里了,給你普及一下,你難道不想以后你的簡(jiǎn)歷上出現(xiàn)——熟悉GC常用算法,熟悉常見(jiàn)垃圾收集器,具有實(shí)際JVM調(diào)優(yōu)實(shí)戰(zhàn)經(jīng)驗(yàn)嗎?保證讓你豁然開(kāi)朗,等你以后去面試的時(shí)候,給面試官講這些保證妥妥的。
  • 小勇:你這么說(shuō)我倒是有點(diǎn)興趣,但是如果講不明白,那你就浪費(fèi)了我時(shí)間了,晚飯就你請(qǐng)吧。我是沒(méi)問(wèn)題,但是我的三個(gè)粉絲不會(huì)答應(yīng)你的小勇:你沒(méi)問(wèn)題就行了,請(qǐng)開(kāi)始你的表演吧~

什么是垃圾

什么是垃圾,就是沒(méi)有任何引用指向的一個(gè)對(duì)象或者多個(gè)對(duì)象(循環(huán)引用),但是他們卻依然占據(jù)著內(nèi)存空間。

GC是一種自動(dòng)的存儲(chǔ)管理機(jī)制。當(dāng)一些被占用的內(nèi)存不再需要時(shí),就應(yīng)該予以釋放。這種存儲(chǔ)資源管理,稱為垃圾回收。

就像我們的衣柜一樣,我們里面可能存放這很多衣服,有可能幾個(gè)月或者幾年都不會(huì)穿過(guò)一次,但是這些我們不穿的衣服一直霸占著我們的衣柜(內(nèi)存),我們把這些不會(huì)穿的衣服扔掉的或者捐贈(zèng)出去,這樣我們就可以放更多可以穿的衣服,這個(gè)就類似于“垃圾回收”。

在GC里面,只分為可回收和不可回收,如下圖所示:

 

1.1 Java 和 C++ 垃圾回收的區(qū)別

Java是你只管扔垃圾就可以,Java會(huì)自動(dòng)幫你處理,而C++要手動(dòng)處理,但是容易造成一個(gè)問(wèn)題就是忘記回收或者回收多次

  • java
  1. GC處理垃圾
  2. 開(kāi)發(fā)效率高,執(zhí)行效率低
  • C++
  1. 手工處理垃圾
  2. 忘記回收,會(huì)導(dǎo)致內(nèi)存泄漏
  3. 回收多次,非法訪問(wèn)
  4. 開(kāi)發(fā)效率,執(zhí)行效率高

怎么找垃圾?

上面我們知道了什么是垃圾,那么我們?nèi)绾稳フ业嚼?

在堆里面存放這Java中幾乎所有的對(duì)象實(shí)例,垃圾收集器在對(duì)堆進(jìn)行回收前,首先要做的事情就是確定這些對(duì)象哪些還 “存活”,哪些是需要進(jìn)行回收的(即不再被引用的對(duì)象)

找到垃圾有兩種算法

  • reference count (引用計(jì)數(shù)算法)
  • Root Searching (根可達(dá)算法)

1. 引用計(jì)數(shù)法

會(huì)給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它的時(shí)候,計(jì)數(shù)器的值就 +1 ,當(dāng)引用失效時(shí),計(jì)數(shù)器值就 -1 ,計(jì)數(shù)器的值為 0 的對(duì)象不可能在被使用,這個(gè)時(shí)候就可以判定這個(gè)對(duì)象是垃圾。

當(dāng)圖中的數(shù)值變成0時(shí),這個(gè)時(shí)候使用引用計(jì)數(shù)算法就可以判定它是垃圾了,但是引用計(jì)數(shù)法不能解決一個(gè)問(wèn)題,就是當(dāng)對(duì)象是循環(huán)引用的時(shí)候,計(jì)數(shù)器值都不為0,這個(gè)時(shí)候引用計(jì)數(shù)器無(wú)法通知GC收集器來(lái)回收他們,如下圖所示:

這個(gè)時(shí)候就需要使用到我們的根可達(dá)算法

2. 根可達(dá)算法

根可達(dá)算法的意思是說(shuō)從根上開(kāi)始搜索,當(dāng)一個(gè)程序啟動(dòng)后,馬上需要的那些個(gè)對(duì)象就叫做根對(duì)象,所謂的根可達(dá)算法就是首先找到根對(duì)象,然后跟著這根線一直往外找到那些有用的,例如我們Java程序 main() 方法運(yùn)行,一個(gè)main() 方法會(huì)啟動(dòng)一個(gè)線程。

線程棧變量: 線程里面會(huì)有線程棧和main棧幀,從這個(gè)main() 里面開(kāi)始的這些對(duì)象都是我們的根對(duì)象。

靜態(tài)變量: 一個(gè)class 它有一個(gè)靜態(tài)的變量,load到內(nèi)存之后馬上就得對(duì)靜態(tài)變量進(jìn)行初始化,所以靜態(tài)變量到的對(duì)象這個(gè)叫做根對(duì)象。

常量池: 如果你這個(gè)class會(huì)用到其他的class的那些個(gè)類的對(duì)象,這些就是根對(duì)象。

JNI: 如果我們調(diào)用了 C和C++ 寫(xiě)的那些本地方法所用到的那些個(gè)類或者對(duì)象

圖中的 object5 和object6 雖然他們之間互相引用了,但是從根找不到它,所以就是垃圾,而object8沒(méi)有任何引用自然而然也是垃圾,其他的Object對(duì)象都有可以從根找到的,所以是有用的,不會(huì)被垃圾回收掉。

3. 區(qū)別

如何清理垃圾

我們找到對(duì)應(yīng)的垃圾之后,我們?nèi)绻デ謇砝?GC常用的算法有三種:

  • Mark-Sweep(標(biāo)記清除)
  • Copying(拷貝)
  • Mark-Compact(標(biāo)記壓縮)

1. 標(biāo)記 - 清除算法

就和它的名字一樣 ,算法分為 “標(biāo)記” 和 “清除” 兩個(gè)階段,首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對(duì)象,這個(gè)是最基礎(chǔ)的收集算法,為什么說(shuō)它是最基礎(chǔ)的,因?yàn)楹罄m(xù)的收集器都是基礎(chǔ)這種思路并對(duì)其不足進(jìn)行改進(jìn)而得到的。

標(biāo)記清除算法它有自己的小問(wèn)題,大家可以看到上面這張圖,我們從GC的根找到那些不可回收的,綠色是不可回收的,紫色是可以回收的,我們把它回收之后就變成空閑的了,這種算法相對(duì)比較簡(jiǎn)單,在存活對(duì)象比較多的情況下效率比較高,它需要經(jīng)歷兩次掃描,第一遍掃描是找到那些有用的,第二遍掃描是把那些沒(méi)用的找出來(lái)清理掉,這里會(huì)有兩個(gè)問(wèn)題:一個(gè)是效率問(wèn)題,標(biāo)記和清除兩個(gè)過(guò)程的效率都不高,另一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的空間碎片,如果空間碎片太多會(huì)導(dǎo)致以后的程序在運(yùn)行過(guò)程中需要分配較大對(duì)象的時(shí)候,無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

2. 復(fù)制算法

為了解決效率的問(wèn)題,所以有了復(fù)制(Copying)算法的出現(xiàn),它將可用的內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊,當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象賦值到另一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉,這樣使得每次都對(duì)整個(gè)半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只需要移動(dòng)堆頂?shù)闹羔?,這種適用于存活對(duì)象較少的情況,所以比較適合eden區(qū),只掃描一次,效率提高了沒(méi)有碎片,但是會(huì)造成空間的浪費(fèi),將內(nèi)存縮小為原來(lái)的一半,未免太高了一點(diǎn),而且移動(dòng)復(fù)制對(duì)象,需要調(diào)整對(duì)象的引用

3. 標(biāo)記 壓縮算法

標(biāo)記壓縮就是把所有的東西整理的過(guò)程,清理的過(guò)程同時(shí)壓縮到頭上去?;厥罩?,有用的往前面走,將剩下的清理出來(lái),但是標(biāo)記壓縮算法依然有它的問(wèn)題,我們都是通過(guò)GC Roots 找到那些不可回收的對(duì)象,然后把不可回收的往前挪,這個(gè)時(shí)候我們需要掃描兩次而且需要移動(dòng)對(duì)象,第一遍掃描出有用的對(duì)象,第二遍進(jìn)行移動(dòng),而且移動(dòng)如果是多線程還需要進(jìn)行同步,所以這個(gè)效率會(huì)低很多,但是它不會(huì)產(chǎn)生碎片,分配對(duì)象也不會(huì)產(chǎn)生內(nèi)存減半。

4. 總結(jié)

  • Mark-Sweep(標(biāo)記清除): 標(biāo)記為垃圾之后就給清理掉,別的空間還是固定的,效率還可以,就是容易產(chǎn)生碎片
  • Copying(拷貝): 將內(nèi)存一分為二,只使用一半,如果垃圾太多了,拷貝有用的到另外一邊,剩下的清理就直接整個(gè)內(nèi)存進(jìn)行清理,效率比較高
  • Mark-Compact(標(biāo)記壓縮): 將所有的對(duì)象湊在一起,把垃圾全部清理走,接下來(lái)剩下的這個(gè)空間還是連續(xù)的,在里面分配任何內(nèi)容的時(shí)候直接往里面分配就行了

堆內(nèi)存邏輯分區(qū)

JVM中的Hot Spot 用的是分代算法

新生代分為:eden、survivor

eden(伊甸): 默認(rèn)比例8:是我們剛剛新 new出來(lái)對(duì)象之后往里扔的那塊區(qū)域survivor: 默認(rèn)比例是1:是回收一次之后跑到這個(gè)區(qū)域,這里面由于裝的對(duì)象不同,所以采取的算法也不同

由于新生代存活對(duì)象特別少,死去對(duì)象特別多所以使用的算法是 復(fù)制算法

old 老年代:tenured(終身)老年代活著的對(duì)象特別多適用于:標(biāo)記清除和標(biāo)記壓縮算法

一個(gè)對(duì)象從出生到消亡

一個(gè)對(duì)象產(chǎn)生之后首先進(jìn)行棧上分配,棧上如果分配不下會(huì)進(jìn)入伊甸區(qū),伊甸區(qū)經(jīng)過(guò)一次垃圾回收之后進(jìn)入surivivor區(qū),survivor區(qū)在經(jīng)過(guò)一次垃圾回收之后又進(jìn)入另外一個(gè)survivor,與此同時(shí)伊甸區(qū)的某些對(duì)象也跟著進(jìn)入另外一個(gè)survivot,什么時(shí)候年齡夠了就會(huì)進(jìn)入old區(qū),這是整個(gè)對(duì)象的一個(gè)邏輯上的移動(dòng)過(guò)程。

那什么時(shí)候會(huì)在棧上分配,什么時(shí)候會(huì)在伊甸區(qū)分配?

1 棧上分配

棧上分配:

  • 線程私有小對(duì)象:小對(duì)象、線程私有的
  • 無(wú)逃逸:就在某一段代碼中使用,除了這段代碼就沒(méi)有人認(rèn)識(shí)它了
  • 支持標(biāo)量替換:意思是用普通的屬性、把普通的類型代替對(duì)象就叫標(biāo)量替換

棧上分配會(huì)比在堆上分配快一點(diǎn),如果在棧上分配不下,會(huì)優(yōu)先進(jìn)行本地分配,也就是 線程本地分配TLAB(Thread local Allocation Buffer): 在伊甸區(qū)很多線程都會(huì)往里面分配對(duì)象,但是分配對(duì)象的時(shí)候我們一定會(huì)進(jìn)行空間的征用,誰(shuí)搶到算誰(shuí)的,多線程的同步,效率就會(huì)降低,所以設(shè)計(jì)了TLAB機(jī)制

  • 占用eden,默認(rèn)為1%,在伊甸區(qū)取用百分之一的空間,這塊空間叫做線程獨(dú)有,分配對(duì)象的時(shí)候首先往線程獨(dú)有的這塊空間進(jìn)行分配
  • 多線程的時(shí)候不用競(jìng)爭(zhēng)eden就可以申請(qǐng)空間,提高效率

2 老年代

對(duì)象什么時(shí)候進(jìn)入老年代?

回收了多少次進(jìn)入老年代?

  • 超過(guò) XX:MaxTenuringThreshold指定次數(shù)(YGC)
  1. Parallel Scavenge 15次進(jìn)入老年代
  2. CMS 6次進(jìn)入老年代
  3. G1 15次進(jìn)入老年代

網(wǎng)上有說(shuō)可以次數(shù)往上調(diào)大,這個(gè)是不可能的

動(dòng)態(tài)年齡判斷

為了能夠適用不同程序的內(nèi)存狀況,虛擬機(jī)并不是永遠(yuǎn)的要求對(duì)象的年齡必須達(dá)到了MaxTenuringThreshold才能晉升老年代,如果在Surivivor空間中相同年齡所有對(duì)象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代,無(wú)需等到MaxTenuringThreshold中要求的年齡。

兩個(gè)Survivor之間拷貝來(lái)拷貝去只要超過(guò)百分之50的時(shí)候把年齡最大的直接放入到old區(qū),也就是不一定非得到15歲。

在s1里面有這么多對(duì)象拷貝到了s2里面超過(guò)百分之50的話,s1里面在加上伊甸區(qū)里面,整個(gè)一個(gè)對(duì)象一下子拷貝到s2里面,經(jīng)過(guò)一次垃圾回收,過(guò)去之后,這個(gè)時(shí)候整個(gè)加起來(lái)對(duì)象已經(jīng)超過(guò)s2的一半了,這里面年齡最大的一些對(duì)象直接進(jìn)入老年區(qū),這個(gè)就叫做動(dòng)態(tài)年輕判斷

大對(duì)象直接進(jìn)入老年代 ,所謂的大對(duì)象是指,需要連續(xù)大量?jī)?nèi)存空間的Java對(duì)象,最典型的大對(duì)象就是那種很長(zhǎng)的字符串以及數(shù)組,經(jīng)常出現(xiàn)大對(duì)象容易導(dǎo)致內(nèi)存還有不少空間的時(shí)候就提前觸發(fā)了垃圾收集來(lái)獲得足夠的連續(xù)內(nèi)存空間

start 先是new一個(gè)對(duì)象,然后在棧上進(jìn)行分配,如果在棧上能夠分配,就分配到棧上,棧直接彈出,彈出結(jié)束,如果在棧上分配不下,判斷對(duì)象是否為大對(duì)象,如果是大對(duì)象,直接進(jìn)入老年代,F(xiàn)GC后結(jié)束如果不是,進(jìn)入線程本地分配(TLAB),不管怎么樣都會(huì)到伊甸區(qū)進(jìn)行GC清除,如果清除完畢,直接結(jié)束,如果沒(méi)有清除完畢,進(jìn)入S1,S1繼續(xù)GC清除,如果年齡到了進(jìn)入old區(qū),如果年齡不夠進(jìn)入S2,然后S2再繼續(xù)GC的清除,要么年齡到了,要么動(dòng)態(tài)年齡達(dá)到

MinorGC/YGC: 年輕代空間耗盡時(shí)觸發(fā)MajorGC/FullGC: 在老年代無(wú)法繼續(xù)分配空間時(shí)觸發(fā),新生代老年代同時(shí)進(jìn)行回收

常見(jiàn)的垃圾回收器

新生代收集器: Serial、ParNew、Parallel Scavenge

老年代收集器: Serial Old、CMS、Parallel Old

新生代和老年代收集器: G1、ZGC、Shenandoah

每種垃圾回收器之間不是獨(dú)立操作的,下圖表示垃圾回收器之間有連線表示,可以協(xié)作使用:

新生代垃圾收集器

1. Serial收集器

Serial 收集器是最基礎(chǔ)、歷史最悠久的收集器,是一個(gè)單線程工作的收集器,它的“單線程”的意義不是說(shuō)他只會(huì)使用一個(gè)處理器或者一條收集線程去完成垃圾收集的工作,更重要的是強(qiáng)調(diào)在它進(jìn)行垃圾收集的時(shí)候,會(huì)暫停其他所有工作線程,直到它收集結(jié)束

 

根據(jù)上圖中我們可以知道,當(dāng)Serial收集器運(yùn)行的時(shí)候,會(huì)暫停所有線程,“Stop The World” ,等到GC完成后,應(yīng)用線程繼續(xù)執(zhí)行,就類似于 你有三個(gè)女朋友,他們同時(shí)讓你陪他們?nèi)ス浣?,你只能陪完其中一個(gè)才能去陪另外一個(gè),陪其中一個(gè)的時(shí)候,其他女朋友就要等待,但是垃圾收集這項(xiàng)工作要比這種情況要復(fù)雜的多!

優(yōu)勢(shì): 因?yàn)槭褂玫氖菃尉€程的方式,所以對(duì)于單個(gè)CPU來(lái)說(shuō),是其他類型收集器中效率最高的一個(gè)

缺點(diǎn): 在用戶不可知、不可控的情況下,暫停所有線程,風(fēng)險(xiǎn)性和體驗(yàn)感不好,讓人比較難接受

使用命令:可以開(kāi)啟Serial 作為新生代收集器

  • -XX:+UserSerialGC #選擇Serial作為新生代垃圾收集器

2. ParNew收集器

ParNew收集器實(shí)質(zhì)上是Serial收集器的多線程并行版本,除了同時(shí)使用多條線程進(jìn)行垃圾收集器之外,其余的比如Serial收集器可用的控制參數(shù)、收集算法、Stop The Wrold 、對(duì)象分配規(guī)則等等都和Serial收集器完全一樣,在多核機(jī)器上,默認(rèn)開(kāi)啟的手機(jī)線程數(shù)和CPU數(shù)量一樣,但是可以通過(guò)參數(shù)進(jìn)行修改

  • -XX:ParallelGCThreads #設(shè)置JVM垃圾收集的線程數(shù)

ParNew收集器除了支持多線程并行收集之外,其他與Serial收集器相比并沒(méi)有太多創(chuàng)新之處,但它 卻是不少運(yùn)行在服務(wù)端模式下的HotSpot虛擬機(jī),尤其是JDK 7之前的遺留系統(tǒng)中首選的新生代收集 器,其中有一個(gè)與功能、性能無(wú)關(guān)但其實(shí)很重要的原因是:除了Serial收集器外,目前只有它能與CMS 收集器配合工作。

優(yōu)點(diǎn):隨著CPU的有效利用,對(duì)于GC時(shí)系統(tǒng)資源的有效利用有好處缺點(diǎn):同Serial一樣的毛病使用場(chǎng)景:ParNew是許多運(yùn)行在Server模式下的虛擬機(jī)中首選的新生代收集器,因?yàn)镃MS只能與Serial 或者 ParNew 配合使用,在如今的多核環(huán)境下,首選的是多線程并行的ParNew,ParNew收集器是激活CMS后 (使用-XX:+UseConcMarkSweepGC選項(xiàng))的默認(rèn)新生代收集器,也可以使用 -XX:+/-UseParNewGC選項(xiàng)來(lái)強(qiáng)制指定或者禁用它

3. Parallel Scavenge收集器

Parallel Scavenge收集器也是一款新生代的收集器,它同樣是基于標(biāo)記-復(fù)制算法那實(shí)現(xiàn)的收集器,也是能夠并行收集器的多線程收集器,Parallel Scavenge收集器關(guān)注點(diǎn)與其他收集器的不用處在于,CMS等收集器的關(guān)注點(diǎn)是盡可能地縮短垃圾收集時(shí)用戶線程的停頓時(shí)間,而Parallel Scavenge收集器的目標(biāo)則是一個(gè)可控制的吞吐量,所謂的吞吐量就是處理器用于運(yùn)行用戶代碼的時(shí)間與處理器總消耗的比值,如下圖所示:

如果說(shuō)虛擬機(jī)完成某個(gè)任務(wù),用戶代碼加上垃圾收集總共耗費(fèi)了100分鐘,其中垃圾收集花掉1分鐘,那么吞吐量就是99%。停頓時(shí)間越短就越適合需要與用戶交互或者需要保證服務(wù)響應(yīng)質(zhì)量的程序,良好的響應(yīng)速度能提升用戶體驗(yàn)。

垃圾收集器每100秒收集一次,每次停頓10秒,和垃圾收集器每50秒收集一次,每次停頓時(shí)間7秒,雖然后者停頓時(shí)間變短了,但是總體吞吐量變低了,CPU總體利用率變低了。

可以通過(guò) -XX:MaxGCPauseMillis來(lái)設(shè)置收集器盡可能在多長(zhǎng)時(shí)間內(nèi)完成內(nèi)存回收,可以通過(guò) -XX:GCTimeRatio來(lái)精確控制吞吐量。

如下是 Parallel 收集器和 Parallel Old 收集器結(jié)合進(jìn)行垃圾收集的示意圖,在新生代,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí),所有線程暫停執(zhí)行,ParNew 收集器以多線程,采用復(fù)制算法進(jìn)行垃圾收集工作,收集完之后,用戶線程繼續(xù)開(kāi)始執(zhí)行;在老年代,當(dāng)用戶線程都執(zhí)行到安全點(diǎn)時(shí),所有線程暫停執(zhí)行,Parallel Old 收集器以多線程,采用標(biāo)記整理算法進(jìn)行垃圾收集工作。

老年代垃圾收集器

1. Serial Old 收集器

Serial Old 是 Serial收集器的老年代版本,它同樣是一個(gè)單線程收集器,使用標(biāo)記-整理算法,這個(gè)收集器的主要意義也是供客戶端模式下HotSpot虛擬機(jī)使用。如果在服務(wù)端一種是與Parallel Scavenge收集器搭配使用,另外一種是作為CMS 收集器發(fā)生失敗時(shí)的后備預(yù)案。

Serial收集器與Serial Old收集器的運(yùn)行示意圖:

適用場(chǎng)景: Client模式;單核服務(wù)器;與Parallel Scavenge收集器搭配;作為CMS收集器的后備方案,在并發(fā)收集發(fā)生Concurrent Mode Failure時(shí)使用

2. Parallel Old收集器

Parallel Old 是 Parallel Scavenge收集器的老年代版本,支持多線程并發(fā)收集,基于標(biāo)記-整理算法實(shí)現(xiàn),可以充分利用多核CPU的計(jì)算能力,慮Parallel Scavenge/Parallel Old收集器運(yùn)行示意圖:

2. CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。CMS收集器是基于標(biāo)記-清楚算法實(shí)現(xiàn)的,這個(gè)收集器的運(yùn)作過(guò)程比前面的幾個(gè)收集器更復(fù)雜一點(diǎn),整個(gè)過(guò)程分為四個(gè)步驟:

1) 初始標(biāo)記(CMS initial mark): 只是標(biāo)記 GC Roots能夠直接關(guān)聯(lián)到的對(duì)象,速度很快

2) 并發(fā)標(biāo)記(CMS concurrent mark): 從GC Roots 的直接關(guān)聯(lián)對(duì)象開(kāi)始遍歷整個(gè)對(duì)象圖的過(guò)程,這個(gè)過(guò)程耗時(shí)較長(zhǎng)但是不需要停頓用戶線程,可以和垃圾收集線程一起并發(fā)運(yùn)行

3) 重新標(biāo)記(CMS remark): 修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作導(dǎo)致標(biāo)記產(chǎn)生對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間會(huì)比初始標(biāo)記階段稍長(zhǎng)一些

4) 并發(fā)清除(CMS concurrent sweep): 清理刪除掉標(biāo)記階段判斷的已經(jīng)死亡的對(duì)象,由于不需要移動(dòng)存活對(duì)象,這個(gè)階段也是可以與用戶線程同時(shí)并發(fā)的。

其中 初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要 “Stop The World” 暫停所有用戶線程,由于在整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清理階段中,垃圾收集器線程都可以與用戶線程一起工作,總體來(lái)說(shuō),CMS收集器的內(nèi)存回收過(guò)程是和用戶線程一起并發(fā)執(zhí)行的,如下圖所示:

優(yōu)點(diǎn):

CMS收集器是一款優(yōu)秀的收集器,它主要體現(xiàn)為:并發(fā)收集、低停頓。

缺點(diǎn):

CMS收集器對(duì)處理器資源非常敏感,在并發(fā)階段,雖然不會(huì)導(dǎo)致用戶線程停頓,但也會(huì)因?yàn)檎加靡徊糠志€程導(dǎo)致應(yīng)用程序變慢,降級(jí)總的吞吐量。CMS默認(rèn)啟動(dòng)回收線程數(shù)是(處理器核心數(shù)量+3)/4,也就是說(shuō)如果處理器核心數(shù)大于等于四個(gè),并發(fā)回收時(shí)垃圾收集線程只占用不超過(guò)25%的處理器運(yùn)算資源,處理器資源會(huì)隨著CPU數(shù)量的增加而下降,但是當(dāng)CPU數(shù)量不足四個(gè)的時(shí)候,CMS對(duì)用戶程序的影響就可能變的很大。CMS收集器無(wú)法處理 “浮動(dòng)垃圾” ,有可能出現(xiàn) “Concurrent Mode Failure” 失敗進(jìn)而導(dǎo)致另一次完全“Stop The World” 的Full GC 的產(chǎn)生,在CMS的并發(fā)標(biāo)記和并發(fā)清理階段,用戶線程是還在繼續(xù)進(jìn)行的,程序在運(yùn)行自然就還會(huì)伴隨有新的垃圾對(duì)象不斷產(chǎn)生,但這一部分垃圾對(duì)象是出現(xiàn)在標(biāo)記過(guò)程結(jié)束以后,CMS無(wú)法在當(dāng)次收集中處理掉它們,只好留待下一次垃圾收集時(shí)再清理掉。這一部分的垃圾就稱為“浮動(dòng)垃圾” 因?yàn)镃MS是一款基于 “標(biāo)記-清除”算法實(shí)現(xiàn)的收集器,因此收集結(jié)束時(shí)會(huì)有大量的空間碎片產(chǎn)生,空間碎片過(guò)多的時(shí),將對(duì)給大對(duì)象帶來(lái)很大的麻煩,有可能不得不提前進(jìn)行Full GC的操作,不過(guò)通過(guò)參數(shù):-XX:+UseCMS-CompactAtFullCollection進(jìn)行優(yōu)化

新生代和老年代垃圾收集器

G1收集器

Garbage First (簡(jiǎn)稱G1)收集器是垃圾收集器技術(shù)發(fā)展歷史上的里程碑式的成果,它開(kāi)創(chuàng)了收集器面向局部收集的設(shè)計(jì)思路和基于Region的內(nèi)存布局形式。

G1收集器是一款面向服務(wù)器端應(yīng)用的垃圾收集器,在JDK9發(fā)布的時(shí)候成為服務(wù)端模式下的默認(rèn)垃圾收集器,而CMS則淪為不被推薦使用的收集器

特點(diǎn):

在G1收集器出現(xiàn)之前所有的其他收集器,目標(biāo)范圍要么是新生代要么是老年代,要么就是Java堆,但是G1做了全面性,它可以面向堆內(nèi)存任何部分來(lái)組成回收集進(jìn)行回收,衡量標(biāo)準(zhǔn)不再是它屬于哪個(gè)分代,而是那塊內(nèi)存中存放的垃圾數(shù)量最多,回收收益最大,這就是G1收集器的Mixed GC模式。而G1開(kāi)創(chuàng)的基于Region的堆內(nèi)存布局是它能夠?qū)崿F(xiàn)這個(gè)目標(biāo)的關(guān)鍵。

雖然G1仍然保留了新生代和老年代的概念,但新生代和老年代不再是固定的,他們都是一系列區(qū)域的動(dòng)態(tài)集合,G1可以建立可預(yù)測(cè)的停頓時(shí)間模型,是因?yàn)樗鼘egion作為單次回收最小單元

G1不再堅(jiān)持固定大小以及固定數(shù)量的分代區(qū)域劃分,而是把連續(xù)的Java堆劃分為多個(gè)大小相等的獨(dú)立區(qū)域,每一個(gè)Region都可以根據(jù)需要,扮演新生代的Eden空間、Survivor空間或者老年代空間,收集器能夠?qū)Π缪莶煌慕巧腞egion采用不同的策略去處理。

Region中海油一類特殊的Humongous區(qū)域,專門(mén)用來(lái)存儲(chǔ)大對(duì)象。G1認(rèn)為只要大小超過(guò)一個(gè)Region容量一半的對(duì)象即可判定為大對(duì)象。

G1收集器的運(yùn)行過(guò)程:

初始標(biāo)記(Initial Marking): 標(biāo)記GC Roots 能直接關(guān)聯(lián)到的對(duì)象,并且修改TAMS指針的值,讓下一階段用戶線程并發(fā)運(yùn)行時(shí),能正確在可用的Region中分配新對(duì)象,需要耗時(shí)較短的停頓線程,但是是借用Minor GC的時(shí)候同步完成的,所以在這個(gè)階段實(shí)際沒(méi)有額外的停頓

并發(fā)標(biāo)記(Concurrent Marking): 從GC Roots 開(kāi)始對(duì)堆中對(duì)象進(jìn)行可達(dá)性分析,遞歸掃描整個(gè)堆里面的對(duì)象圖,找出要回收的對(duì)象,這個(gè)階段耗時(shí)較長(zhǎng),但可以和用戶程序并發(fā)執(zhí)行。

最終標(biāo)記(Final Marking): 對(duì)用戶線程做另一個(gè)短暫的暫停,用戶處理并發(fā)階段結(jié)束后仍遺留下來(lái)的最后那少量的SATB記錄

篩選回收(Live Data Counting and Evacuation): 負(fù)責(zé)更新Region的統(tǒng)計(jì)數(shù)據(jù),對(duì)各個(gè)Region的回收價(jià)值和成本進(jìn)行排序,根據(jù)用戶鎖期望的停頓時(shí)間來(lái)制定回收計(jì)劃,可以只有選擇任意多個(gè)Region構(gòu)成回收集,然后把決定回收的那一部分Region的存活對(duì)象賦值到空的Region中,再清理整個(gè)Region的全部空間。

總結(jié)

小勇你懂了嗎?小勇小勇,你別睡著了啊,我還沒(méi)講完呢!小勇醒醒啊!!!

小勇迷迷糊糊的說(shuō):怎么了,下班了嗎?。。。。,下班啥,我講的GC你聽(tīng)懂了嗎?

小勇:聽(tīng)懂了,我明天就去面試,你講的太棒了!

敷衍,算了我已經(jīng)把東西都放在筆記里面了,你要是感興趣就可以來(lái)看看。

本文轉(zhuǎn)載自微信公眾號(hào)「牧小農(nóng)」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系牧小農(nóng)公眾號(hào)。

 

責(zé)任編輯:姜華 來(lái)源: 牧小農(nóng)
相關(guān)推薦

2020-10-31 09:06:37

C語(yǔ)言編程語(yǔ)言

2013-08-09 10:37:31

代碼數(shù)據(jù)

2015-05-19 14:30:48

加密視頻加密億賽通

2024-07-05 11:47:43

2013-07-22 11:06:37

2013-12-27 09:46:40

Windows 9Windows 9桌面

2021-03-17 11:47:37

tomcatJavaServerJava

2015-06-24 16:09:54

Easy Connec深信服

2020-04-02 07:31:53

RPC超時(shí)服務(wù)端

2025-04-02 00:45:00

JupyterDrawData數(shù)據(jù)

2021-11-02 11:31:47

Go代碼模式

2021-12-13 22:52:37

iphone iOSHTML

2015-12-15 10:33:59

域名網(wǎng)絡(luò)域名

2021-12-29 10:21:41

Linux 權(quán)限擴(kuò)展名

2021-07-05 18:05:40

SpringBean方法

2017-02-09 19:45:07

Linux系統(tǒng)Linux 發(fā)行版

2020-01-06 09:14:59

Java程序員線程

2021-09-06 07:58:47

鏈表數(shù)據(jù)結(jié)構(gòu)

2016-12-21 12:19:57

AR廣告奧迪

2018-06-26 16:31:45

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)