java宝典app,总结到位
垃圾回收算法
垃圾回收算法的實(shí)現(xiàn)設(shè)計到大量的程序細(xì)節(jié),并且每一個平臺的虛擬機(jī)操作內(nèi)存的方式都有不同,所以不需要去了解算法的具體實(shí)現(xiàn)。
復(fù)制算法
將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉。這樣使得每次都是對整個半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要按順序分配內(nèi)存即可,實(shí)現(xiàn)簡單,運(yùn)行高效。
只是這種算法的代價是將內(nèi)存縮小為了原來的一半。但是要注意:內(nèi)存移動是必須實(shí)打?qū)嵉囊苿?#xff08;復(fù)制),所以對應(yīng)的引用(直接指針)需要調(diào)整。
復(fù)制回收算法適合于新生代,因?yàn)榇蟛糠謱ο蟪λ?#xff0c;那么復(fù)制過去的對象比較少,效率自然就高,另外一半的一次性清理是很快的。
Appel式回收
一種更加優(yōu)化的復(fù)制回收分代策略:具體做法是分配一塊較大的 Eden 區(qū)和兩塊較小的 Survivor 空間(一般稱作做From區(qū)和To區(qū),也可以叫做S0和S1)
基于經(jīng)驗(yàn)統(tǒng)計,新生代中的對象98%是“朝生夕死”的,所以并不需要按照 1:1 的比例來劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden和其中一塊Survivor[1]。當(dāng)回收時,將 Eden 和 Survivor 中還存活著的對象一次性地復(fù)制到另外一塊 Survivor 空間上, 最后清理掉 Eden 和剛才用過的 Survivor 空間。
HotSpot 虛擬機(jī)默認(rèn) Eden 和 Survivor 的大小比例是 8:1,也就是每次新生代中可用內(nèi)存空間為整個新生代容量的 90%(80%+10%),只有10%的內(nèi)存會被 “浪費(fèi)”。當(dāng)然,98%的對象可回收只是一般場景下的數(shù)據(jù),我們沒有辦法保證每次回收都只有不多于10%的對象存活,當(dāng) Survivor 空間不夠用時,需要依賴其他內(nèi)存(這里指老年代)進(jìn)行分配擔(dān)保(Handle Promotion)
標(biāo)記清除
算法分為“標(biāo)記”和“清除”兩個階段:首先掃描所有對象標(biāo)記出需要回收的對象,在標(biāo)記完成后掃描回收所有被標(biāo)記的對象,所以需要掃描兩遍。回收效率略低,如果大部分對象是朝生夕死,那么回收效率降低,因?yàn)樾枰罅繕?biāo)記對象和回收對象,對比復(fù)制回收效率要低。
它的主要問題,標(biāo)記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致以后在程序運(yùn)行過程中需要分配較大對象時,無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾回收動作。回收的時候如果需要回收的對象越多,需要做的標(biāo)記和清除的工作越多,所以標(biāo)記清除算法適用于老年代。
標(biāo)記整理
首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后,后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存。標(biāo)記整理算法雖然沒有內(nèi)存碎片,但是效率偏低。
我們看到標(biāo)記整理與標(biāo)記清除算法的區(qū)別主要在于對象的移動。對象移動不單單會加重系統(tǒng)負(fù)擔(dān),同時需要全程暫停用戶線程才能進(jìn)行,同時所有引用對象的地方都需要更新(直接指針需要調(diào)整)。所以看到,老年代采用的標(biāo)記整理算法與標(biāo)記清除算法,各有優(yōu)點(diǎn),各有缺點(diǎn)。
垃圾回收器
回收器名稱回收對象和算法回收器類型Serial新生代,復(fù)制算法線程(串行)Parallel Scavenge新生代,復(fù)制算法并行的多線程回收器ParNew新生代,復(fù)制算法并行的多線程回收器Serial Old老年代,標(biāo)記整理算法單線程(串行)Parallel Old老年代,標(biāo)記整理算法并行的多線程回收器CMS老年代,標(biāo)記清除算法并發(fā)的多線程回收器G1新生代,老年代;標(biāo)記整理 + 化整為零并發(fā)的多線程回收器
目前最常用的兩種垃圾回收器,也不用多說,肯定是CMS和G1,一般面試官會問下CMS和G1的區(qū)別以及各自的特點(diǎn),不太會深入問實(shí)現(xiàn)原理,畢竟Java面試可問的知識點(diǎn)實(shí)在太多了,都一個個深入問1個小時的面試時間根本不夠。
串行的垃圾回收器就不說了,這里專門講下并發(fā)的垃圾回收器
CMS(Concurrent Mark Sweep)回收器
顧名思義,這是并發(fā)的垃圾回收器,這種回收器是一種以獲取最短的回收停頓時間為目的的垃圾收集器,目前很大一部分Java的互聯(lián)網(wǎng)應(yīng)用或者B/S系統(tǒng)的服務(wù)器上,由于這類應(yīng)用尤其在意相應(yīng)速度,希望系統(tǒng)停頓時間越短越好,這樣用戶體驗(yàn)也會更好,CMS就非常符合這類應(yīng)用的需求。
從名字就可以看出,這種回收器是基于標(biāo)記清除的算法實(shí)現(xiàn),它的運(yùn)作過程相對串行的垃圾回收器相對復(fù)雜點(diǎn),分為以下4個步驟
初始標(biāo)記:很短,僅僅只是標(biāo)記下GC Root能直接關(guān)聯(lián)的對象,速度極快。
并發(fā)標(biāo)記:和用戶應(yīng)用同時進(jìn)行,進(jìn)行GC Root跟蹤的過程,標(biāo)記GC Root開始關(guān)聯(lián)的所有對象,開始遍歷整個可達(dá)分析的路徑對象,這個時間比較長,所以并發(fā)。
重新標(biāo)記:短暫,為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,這個階段的停頓時間一般會比初始標(biāo) 記階段稍長一些,但遠(yuǎn)比并發(fā)標(biāo)記的時間短。
并發(fā)清除:由于整個過程中耗時最長的并發(fā)標(biāo)記和并發(fā)清除過程收集器線程都可以與用戶線程一起工作,所以,一般來說,CMS 的內(nèi)存回收過程是與用戶線程一起執(zhí)行的。-XX:+UseConcMarkSweepGC ,表示新生代使用ParNew,老年代的用 CMS。
CPU 敏感:CMS 對處理器資源敏感,畢竟采用了并發(fā)的收集、當(dāng)處理核心數(shù)不足 4 個時,CMS 對用戶的影響較大。
浮動垃圾:由于 CMS 并發(fā)清理階段用戶線程還在運(yùn)行著,伴隨程序運(yùn)行自然就還會有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過程之后,CMS無法在當(dāng)次收集中處理掉它們,只好留待下一次GC時再清理掉。這一部分垃圾就稱為“浮動垃圾”。由于浮動垃圾的存在,因此需要預(yù)留出一部分內(nèi)存,意味著 CMS 收集不能像其它收集器那樣等待老年代快滿的時候再回收。在1.6的版本中老年代空間使用率閾值(92%)如果預(yù)留的內(nèi)存不夠存放浮動垃圾,就會出現(xiàn) Concurrent Mode Failure,這時虛擬機(jī)將臨時啟用 Serial Old 來替代 CMS。
會產(chǎn)生空間碎片:標(biāo)記 - 清除算法會導(dǎo)致產(chǎn)生不連續(xù)的空間碎片總體來說,CMS是JVM 推出了第一款并發(fā)垃圾收集器,所以還是非常有代表性。但是最大的問題是 CMS 采用了標(biāo)記清除算法,所以會有內(nèi)存碎片,當(dāng)碎片較多時,給大對象的分配帶來很大的麻煩,為了解決這個問題,CMS 提供一個 參數(shù):-XX:+UseCMSCompactAtFullCollection,一般是開啟的,如果分配不了大對象,就進(jìn)行內(nèi)存碎片的整理過程。這個地方一般會使用 Serial Old ,因?yàn)?Serial Old 是一個單線程,所以如果內(nèi)存空間很大、且對象較多時,CMS 發(fā)生這樣情況會很卡。
總結(jié):CMS 問題比較多,所以JDK沒有一個版本默認(rèn)垃圾回收器是CMS,只能手動指定。但是它畢竟是第一個并發(fā)垃圾回收器,對于了解并發(fā)垃圾回收具有一定意義,所以我們必須了解。為什么 CMS 采用標(biāo)記-清除,在實(shí)現(xiàn)并發(fā)的垃圾回收時,如果采用標(biāo)記整理算法,那么還涉及到對象的移動(對象的移動必定涉及到引用的變化,這個需要暫停業(yè)務(wù)線程來處理?xiàng)P畔?#xff0c;這樣使得并發(fā)收集的暫停時間更長),所以使用簡單的標(biāo)記-清除算法才可以降低 CMS的STW的時間。
該垃圾回收器適合回收堆空間幾個 G至20G。
G1(Garbage First)
隨著JVM內(nèi)存的增大,STW的時間成為JVM 急迫解決的問題,但是如果按照傳統(tǒng)的分代模型,總跳不出STW時間不可預(yù)測這點(diǎn)。
為了實(shí)現(xiàn)STW的時間可預(yù)測,首先要有一個思想上的改變。
G1將堆內(nèi)存“化整為零”,將堆內(nèi)存劃分成多個大小相等獨(dú)立區(qū)域(Region),每一個Region 都可以根據(jù)需要,扮演新生代的Eden空間、Survivor空間,或者老年代空間。
回收器能夠?qū)Π缪莶煌巧?Region 采用不同的策略去處理,這樣無論是新創(chuàng)建的對象還是已經(jīng)存活了一段時間、熬過多次收集的舊對象都能獲取很好的收集效果。
Region:Region可能是Eden,也有可能是Survivor,也有可能是Old,另外 Region 中還有一類特殊的Humongous區(qū)域,專門用來存儲大對象。G1認(rèn)為只要大小超過了一個Region容量一半的對象即可判定為大對象。每個Region的大小可以通過參數(shù)-XX:G1HeapRegionSize 設(shè)定,取值范圍為 1MB至32MB,且應(yīng)為2的N次冪。而對于那些超過了整個 Region 容量的超級大對象,將會被存放在 N 個連續(xù)的 Humongous Region 之中,G1 的進(jìn)行回收大多數(shù)情況下都把 Humongous Region 作為老年代的一部分來進(jìn)行看待。
開啟參數(shù)?-XX:+UseG1GC分區(qū)大小?-XX:+G1HeapRegionSize一般建議逐漸增大該值,隨著 size 增加,垃圾的存活時間更長,GC 間隔更長,但每次 GC 的時間也會更長。
最大GC暫停時間?-XX:MaxGCPauseMillis設(shè)置最大GC暫停時間的目標(biāo)(單位毫秒),這是個軟目標(biāo),JVM會盡最大可能實(shí)現(xiàn)它。
運(yùn)行過程如下:
初始標(biāo)記:僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對象,并且修改 TAMS 指針的值,讓下一階段用戶線程并發(fā)運(yùn)行時,能正確地在可用的 Region 中分配新對象。這個階段需要停頓線程,但耗時很短,而且是借用進(jìn)行Minor GC的時候同步完成的,所以G1收集器在這個階段實(shí)際并沒有額外的停頓。要達(dá)到GC與用戶線程并發(fā)運(yùn)行,必須要解決回收過程中新對象的分配,所以G1為每一個Region 區(qū)域設(shè)計了兩個名為TAMS(Top at Mark Start)的指針,從 Region 區(qū)域劃出一部分空間用于記錄并發(fā)回收過程中的新對象。這樣的對象認(rèn)為它們是存活的,不納入垃圾回收范圍。
并發(fā)標(biāo)記:從GC Root開始對堆中對象進(jìn)行可達(dá)性分析,遞歸掃描整個堆里的對象圖,找出要回收的對象,這階段耗時較長,但可與用戶程序并發(fā)執(zhí)行。當(dāng)對象圖掃描完成以后,并發(fā)時有引用變動的對象,這些對象會漏標(biāo),漏標(biāo)的對象會被一個叫做SATB(snapshot at the beginning)算法來解決。
最終標(biāo)記:對用戶線程做另一個短暫的暫停,用于處理并發(fā)階段結(jié)后仍遺留下來的最后那少量的 SATB 記錄(漏標(biāo)對象)。
篩選回收:負(fù)責(zé)更新Region的統(tǒng)計數(shù)據(jù),對各個Region的回收價值和成本進(jìn)行排序,根據(jù)用戶所期望的停頓時間來制定回收計劃,可以自由選擇任意多個Region構(gòu)成回收集,然后把決定回收的那一部分 Region 的存活對象復(fù)制到空的Region中,再清理掉整個舊 Region 的全部空間。這里的操作涉及存活對象的移動,是必須暫停用戶線程,由多條收集器線程并行完成的。
總結(jié):并行與并發(fā):G1 能充分利用多 CPU、多核環(huán)境下的硬件優(yōu)勢,使用多個 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓的時間,部分其他收集器 原本需要停頓 Java 線程執(zhí)行的 GC 動作,G1 收集器仍然可以通過并發(fā)的方式讓 Java 程序繼續(xù)執(zhí)行。
分代收集:與其他收集器一樣,分代概念在 G1 中依然得以保留。雖然 G1 可以不需要其他收集器配合就能獨(dú)立管理整個 GC 堆,但它能夠采用不同的方式 去處理新創(chuàng)建的對象和已經(jīng)存活了一段時間、熬過多次 GC 的舊對象以獲取更好的收集效果。
空間整合:與 CMS 的“標(biāo)記—清理”算法不同,G1 從整體來看是基于“標(biāo)記—整理”算法實(shí)現(xiàn)的收集器,從局部(兩個 Region 之間)上來看是基于“復(fù) 制”算法實(shí)現(xiàn)的,但無論如何,這兩種算法都意味著 G1 運(yùn)作期間不會產(chǎn)生內(nèi)存空間碎片,收集后能提供規(guī)整的可用內(nèi)存。這種特性有利于程序長時間運(yùn) 行,分配大對象時不會因?yàn)闊o法找到連續(xù)內(nèi)存空間而提前觸發(fā)下一次 GC。
追求停頓時間:-XX:MaxGCPauseMillis 指定目標(biāo)的最大停頓時間,G1 嘗試調(diào)整新生代和老年代的比例,堆大小,晉升年齡來達(dá)到這個目標(biāo)時間。
并發(fā)標(biāo)記
三色標(biāo)記算法
說到并發(fā)標(biāo)記,就不能不提下并發(fā)標(biāo)記中的三色標(biāo)記算法,它是一種描述追蹤式回收器的有效的辦法,利用它可以推演回收器的正確性。
在三色標(biāo)記法之前有一個算法叫 Mark-And-Sweep(標(biāo)記清除)。這個算法會設(shè)置一個標(biāo)志位來記錄對象是否被使用。最開始所有的標(biāo)記位都是0,如果發(fā)現(xiàn)對象是可達(dá)的就會置為1,一步步下去就會呈現(xiàn)一個類似樹狀的結(jié)果。等標(biāo)記的步驟完成后,會將未被標(biāo)記的對象統(tǒng)一清理,再次把所有的標(biāo)記位 設(shè)置成0方便下次清理。
這個算法最大的問題是 GC 執(zhí)行期間需要把整個程序完全暫停,不能異步進(jìn)行 GC 操作。因?yàn)樵诓煌A段標(biāo)記清掃法的標(biāo)志位0和1有不同的含義,那么新增的對象無論標(biāo)記為什么都有可能意外刪除這個對象。對實(shí)時性要求高的系統(tǒng)來說,這種需要長時間掛起的標(biāo)記清掃法是不可接受的。所以就需要一個算法來解決 GC 運(yùn)行時程序長時間掛起的問題,那就三色標(biāo)記法。三色標(biāo)記最大的好處是可以異步執(zhí)行,從而可以以中斷時間極少的代價或者完全沒有中斷來進(jìn)行整個GC。
我們將對象分為三種類型:
黑色:根對象,或者該對象與它的子對象都被掃描過。
灰色:對本身被掃描,但是還沒掃描完該對象的子對象。
白色:未被掃描對象,如果掃描完所有對象之后,最終為白色的為不可達(dá)對象,既垃圾對象。
總結(jié)
至此,文章終于到了尾聲。總結(jié)一下,我們談?wù)摿撕啔v制作過程中需要注意的以下三個部分,并分別給出了一些建議:
以及最后為大家準(zhǔn)備的福利時間:簡歷模板+Java面試題+熱門技術(shù)系列教程視頻
戳這里免費(fèi)領(lǐng)取文中資料
TbFJ1cmN1R2dB)**
[外鏈圖片轉(zhuǎn)存中…(img-57gfMVwI-1624773084419)]
[外鏈圖片轉(zhuǎn)存中…(img-O1AlrTaY-1624773084420)]
[外鏈圖片轉(zhuǎn)存中…(img-fnVCeAXr-1624773084420)]
總結(jié)
以上是生活随笔為你收集整理的java宝典app,总结到位的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Unity 之 ShaderGraph
- 下一篇: 黑群晖安装ZEROTIER ONE实现内