?
- 博主簡介:想進(jìn)大廠的打工人
- 博主主頁:@xyk:
- 所屬專欄:?JavaEE初階?
上篇文章我們講了java運(yùn)行時內(nèi)存的各個區(qū)域。
傳送門:【JavaEE】JVM的組成及類加載過程_xyk:的博客-CSDN博客
對于程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧這三部分區(qū)域而言,其生命周期與相關(guān)線程有關(guān),隨線程而生,隨線程而滅。并且這三個區(qū)域的內(nèi)存分配與回收具有確定性,因?yàn)楫?dāng)方法結(jié)束或者線程結(jié)束時,內(nèi)存就自然跟著線程回收了。因此我們本篇所講的有關(guān)內(nèi)存分配和回收關(guān)注的為Java堆與方法區(qū)這兩個區(qū)域。
目錄
文章目錄
一、垃圾回收機(jī)制—GC
1.1 引用計(jì)數(shù)器算法(java沒有采用這種算法)
1.2 可達(dá)性分析
二、垃圾回收算法
2.1 標(biāo)記清除算法
2.2 復(fù)制算法
2.3 標(biāo)記整理算法
2.4 復(fù)合策略”分帶回收“
三、垃圾收集器
3.1 CMS收集器(老年代收集器,并發(fā)GC)
3.2 G1收集器(唯一一款全區(qū)域的垃圾回收器)
一、垃圾回收機(jī)制—GC
在JVM中存在一個垃圾回收機(jī)制,GC,幫助程序猿自動釋放內(nèi)存的,能夠有效的減少內(nèi)存泄漏的出現(xiàn)頻率。主要是針對 堆上的對象 來進(jìn)行釋放~
GC也就是以 對象 為單位進(jìn)行釋放的(說是釋放內(nèi)存,其實(shí)是釋放對象)
GC中主要分成兩個階段:
1.找,誰是垃圾
2.釋放,用什么算法
Java堆中存放著幾乎所有的對象實(shí)例,垃圾回收器在對堆進(jìn)行垃圾回收前,首先要判斷這些對象哪些還存活,哪些已經(jīng)"死去"。判斷對象是否已"死"有如下幾種算法
死亡對象的判斷算法:
1.1 引用計(jì)數(shù)器算法(java沒有采用這種算法)
引用計(jì)算器判斷對象是否存活的算法是這樣的:給每一個對象設(shè)置一個引用計(jì)數(shù)器,每當(dāng)有一個地方引用這個對象的時候,計(jì)數(shù)器就加1,與之相反,每當(dāng)引用失效的時候就減1。當(dāng)計(jì)數(shù)器為0,則認(rèn)為沒有對象了,就是垃圾了~
優(yōu)點(diǎn):實(shí)現(xiàn)簡單、性能高。
缺點(diǎn):增減處理頻繁消耗cpu計(jì)算、計(jì)數(shù)器占用很多位浪費(fèi)空間(每個對象都需要分配一個計(jì)數(shù)器)、最重要的缺點(diǎn)是無法解決循環(huán)引用的問題。
什么是循環(huán)引用?
存在兩個對象,同時互相引用指向?qū)ο?/p>
?
此時,如果a和b都銷毀了,這個時候,兩個對象的引用計(jì)數(shù)給自減1,但是這兩個對象的引用計(jì)數(shù)不是0,不能作為垃圾,無法回收,這倆個對象也無法使用了~陷入了一個邏輯上的循環(huán)
1.2 可達(dá)性分析
把對象之間的引用關(guān)系,理解成了一個樹形結(jié)構(gòu),從一些特殊的起點(diǎn)出發(fā),進(jìn)行遍歷,只要能遍歷訪問到的對象,就是”可達(dá)“,再把”不可達(dá)的“當(dāng)作垃圾回收即可
通過 root 這個引用,就可以訪問到整個樹的任意結(jié)點(diǎn),那么哪些對象可以作為gcroot?
- 棧上的局部變量(每個棧的每個局部變量,都是起點(diǎn))
- 常量池中的引用的對象
- 方法區(qū)中,靜態(tài)成員引用的對象
可達(dá)性分析,克服了引用計(jì)數(shù)的兩個缺點(diǎn)~
但是也有自己的問題:
1.消耗更多的時間,因此某個對象成了垃圾,也不一定第一時間發(fā)現(xiàn),每次掃描的過程,都是需要消耗時間的
2.在進(jìn)行可達(dá)性分析的時候,要順藤摸瓜,一旦這個過程中,代碼的對象引用關(guān)系發(fā)生變化了,就麻煩了
因?yàn)椋瑸榱烁鼫?zhǔn)確的完成這個”摸瓜“的過程,需要讓其他的業(yè)務(wù)線程 暫停工作?。。⊿TW問題
stop the world,為了保證內(nèi)存的一致性,必須先暫停程序的執(zhí)行)
二、垃圾回收算法
上面我們可以通過可達(dá)性分析找到垃圾,那么我們應(yīng)該用什么回收算法來進(jìn)行回收操作呢?
2.1 標(biāo)記清除算法
標(biāo)記-清除算法是最基礎(chǔ)的算法,像它的名字一樣算法分為“標(biāo)記”和“清除”兩個階段,首先需要標(biāo)記出所需要回收的對象,標(biāo)記完成后統(tǒng)一收集被標(biāo)記的對象。
優(yōu)點(diǎn): 實(shí)現(xiàn)簡單。
缺點(diǎn): 產(chǎn)生不連續(xù)的內(nèi)存碎片;“標(biāo)記”和“清除”的執(zhí)行效率都不高。如果要申請空間比較大,會失敗。
2.2 復(fù)制算法
"復(fù)制"算法是為了解決"標(biāo)記-清理"的效率問題。它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這塊內(nèi)存需要進(jìn)行垃圾回收時,會將此區(qū)域還存活著的對象復(fù)制到另一塊上面,然后再把已經(jīng)使用過的內(nèi)存區(qū)域一次清理掉。
?
優(yōu)點(diǎn): 執(zhí)行效率高。
缺點(diǎn): 空間利用率低, 因?yàn)閺?fù)制算法每次只能使用一半的內(nèi)存。
2.3 標(biāo)記整理算法
類似于 順序表刪除中間元素,有一個搬運(yùn)過程~
?
優(yōu)點(diǎn): 解決了內(nèi)存碎片問題,比復(fù)制算法空間利用率高。
缺點(diǎn): 因?yàn)橛芯植繉ο笠苿?,相對效率不高?/p>
2.4 復(fù)合策略”分帶回收“
因此,實(shí)際上 JVM 的實(shí)現(xiàn)思路,是結(jié)合了上述幾種思想方法,針對不同的情況,使用不同的策略.
給對象設(shè)定了”年齡“這樣的概念,描述了這個對象存在多久了,如果一個對象剛誕生,認(rèn)為是0歲,每次經(jīng)過一輪掃描,沒被標(biāo)記成垃圾,這個時候?qū)ο缶蜐q一歲,通過年齡來區(qū)分這個對象的 存活時間
1.新創(chuàng)建的對象,放到伊甸區(qū),當(dāng)垃圾回收掃描伊甸區(qū)之后,絕大部分對象都會在第一輪 GC 中就被干掉~~ 大部分對象是活不過一歲的,朝生夕死
2.如果伊甸區(qū)的對象,熬過第一輪 GC ,就會通過復(fù)制算法,拷貝到幸存區(qū),幸存區(qū)分成兩半大小,一次只使用其中的一半~~ 垃圾回收掃描幸存區(qū)的對象,也是發(fā)現(xiàn)垃圾就淘汰,不是垃圾的,通過復(fù)制算法,復(fù)制到幸存區(qū)的另外一半
3.當(dāng)這個對象在幸存區(qū),熬過若干輪 GC 之后,年齡增長到一定程度了,就會通過復(fù)制算法拷貝到老年代
4.進(jìn)入老年代的對象,年齡都很大了,再消亡的概率比前面新生代中的對象小,針對老年代 GC 的掃描頻次就會降低很多,如果老年代發(fā)現(xiàn)某個對象是垃圾了,使用標(biāo)記整理的方式清除
5.特殊情況,如果對象非常大,直接進(jìn)入老年代(大對象進(jìn)行復(fù)制算法,成本比較高,而且大對象也不會很多)
三、垃圾收集器
3.1 CMS收集器(老年代收集器,并發(fā)GC)
特性:
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標(biāo)的收集器。目前
很大一部分的Java應(yīng)用集中在互聯(lián)網(wǎng)站或者B/S系統(tǒng)的服務(wù)端上,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速
度,希望系統(tǒng)停頓時間最短,以給用戶帶來較好的體驗(yàn)。CMS收集器就非常符合這類應(yīng)用的需求。
CMS收集器是基于“標(biāo)記—清除”算法實(shí)現(xiàn)的,它的運(yùn)作過程相對于前面幾種收集器來說更復(fù)雜一些,整個過程分為4個步驟:
初始標(biāo)記(CMS initial mark)
初始標(biāo)記僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象,速度很快,需要“Stop The
World”。
并發(fā)標(biāo)記(CMS concurrent mark)
并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing的過程。
重新標(biāo)記(CMS remark)
重新標(biāo)記階段是為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分
對象的標(biāo)記記錄,這個階段的停頓時間一般會比初始標(biāo)記階段稍長一些,但遠(yuǎn)比并發(fā)標(biāo)記的
時間短,仍然需要“Stop The World”。
并發(fā)清除(CMS concurrent sweep)
并發(fā)清除階段會清除對象
由于整個過程中耗時最長的并發(fā)標(biāo)記和并發(fā)清除過程收集器線程都可以與用戶線程一起工作,所以,從總體上來說,CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的
缺點(diǎn):
- CMS收集器對CPU資源非常敏感
- CMS收集器無法處理浮動垃圾?
- CMS收集器會產(chǎn)生大量空間碎片
?
3.2 G1收集器(唯一一款全區(qū)域的垃圾回收器)
G1(Garbage First)垃圾回收器是用在heap memory很大的情況下,把heap劃分為很多很多的
region塊,然后并行的對其進(jìn)行垃圾回收
G1垃圾回收器回收region(區(qū)域)的時候基本不會STW,而是基于 most garbage優(yōu)先回收(整體來看是基于"標(biāo)記-整理"算法,從局部(兩個region之間)基于"復(fù)制"算法) 的策略來對region進(jìn)行垃圾回收的。
一個region有可能屬于Eden,Survivor或者Tenured內(nèi)存區(qū)域。圖中的E表示該region屬于Eden內(nèi)存區(qū)域,S表示屬于Survivor內(nèi)存區(qū)域,T表示屬于Tenured內(nèi)存區(qū)域。圖中空白的表示未使用的內(nèi)存空間。G1垃圾收集器還增加了一種新的內(nèi)存區(qū)域,叫做Humongous內(nèi)存區(qū)域,如圖中的H塊。這種內(nèi)存區(qū)域主要用于存儲大對象-即大小超過一個region大小的50%的對象
1.年輕代垃圾收集
在G1垃圾收集器中,年輕代的垃圾回收過程使用復(fù)制算法。把Eden區(qū)和Survivor區(qū)的對象復(fù)制到新的Survivor區(qū)域
?2.老年代垃收集
1.初始標(biāo)記(Initial Mark)階段 - 同CMS垃圾收集器的Initial Mark階段一樣,G1也需要暫停應(yīng)用程序的執(zhí)行,它會標(biāo)記從根對象出發(fā),在根對象的第一層孩子節(jié)點(diǎn)中標(biāo)記所有可達(dá)的對象。
2.并發(fā)標(biāo)記(Concurrent Mark)階段 - 在這個階段G1做的事情跟CMS一樣。但G1同時還多做了一件事情,就是如果在Concurrent Mark階段中,發(fā)現(xiàn)哪些Tenured region中對象的存活率
很小或者基本沒有對象存活,那么G1就會在這個階段將其回收掉,而不用等到后面的clean
up階段。
?3.最終標(biāo)記(CMS中的Remark階段) - 在這個階段G1做的事情跟CMS一樣, 但是采用的算法不
同,G1采用一種叫做SATB(snapshot-at-the-begining)的算法能夠在Remark階段更快的標(biāo)
記可達(dá)對象。4.篩選回收(Clean up/Copy)階段 - 在G1中,沒有CMS中對應(yīng)的Sweep階段。相反 它有一個
Clean up/Copy階段,在這個階段中,G1會挑選出那些對象存活率低的region進(jìn)行回收,這個
階段也是和minor gc一同發(fā)生的,如下圖所示
文章來源:http://www.zghlxwxcb.cn/news/detail-577225.html
G1(Garbage-First)是一款面向服務(wù)端應(yīng)用的垃圾收集器。HotSpot開發(fā)團(tuán)隊(duì)賦予它的使命是未來可以替換掉JDK 1.5中發(fā)布的CMS收集器。 如果你的應(yīng)用追求低停頓,G1可以作為選擇;如果你的應(yīng)用追求吞吐量,G1并不帶來特別明顯的好處。
文章來源地址http://www.zghlxwxcb.cn/news/detail-577225.html
到了這里,關(guān)于【JAVAEE】JVM中垃圾回收機(jī)制 GC的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!