推薦閱讀:我憑借這份pdf拿下了螞蟻金服、字節(jié)跳動、小米等大廠的offer
Java GC垃圾回收幾乎是面試必問的JVM問題之一,本篇文章帶領大家了解Java GC的底層原理,圖文并茂,突破學習及面試瓶頸。
創(chuàng)新互聯(lián)網(wǎng)站建設公司提供網(wǎng)站設計和自適應建站服務。團隊由有經(jīng)驗的網(wǎng)頁設計師、程序員和市場專家組成,能夠提供從H5技術(shù),網(wǎng)站制作,廣告投放平臺,模板建站到微信平臺小程序開發(fā)等全方位服務。 以客戶為中心,致力于為客戶提供創(chuàng)新、高效的解決方案,幫助您打造成功的企業(yè)網(wǎng)站。
JVM中堆的結(jié)構(gòu)圖
圖中展示了堆中三個區(qū)域:Eden、From Survivor、To Survivor。從圖中可以也可以看到它們的大小比例,準確來說是:8:1:1。為什么要這樣設計呢,本篇文章后續(xù)會給出解答,還是根據(jù)垃圾回收的具體情況來設計的。
還記得在設置JVM時,常用的類似-Xms和-Xmx等參數(shù)嗎?對的它們就是用來說設置堆中各區(qū)域的大小的。
控制參數(shù)詳解:
對照上面兩個圖,再來看這些參數(shù)是不是沒有之前那么枯燥了,它們在圖中都有了對應的位置。
有沒有發(fā)現(xiàn)沒有直接設置老年代空間大小的參數(shù)?我們通過簡單的計算獲得。
老年代空間大小=堆空間大小-年輕代大空間大小
對上面參數(shù)立即了,但記憶有困難?那么,以下幾個助記詞可能更好的幫你記憶和理解參數(shù)的含義。
Xmx(memory maximum), Xms(memory startup), Xmn(memory nursery/new), Xss(stack size)。
對于參數(shù)的格式可以這樣理解:
垃圾收集(Garbage Collection)通常被稱為“GC”,由虛擬機“自動化”完成垃圾回收工作。
思考一個問題,既然GC會自動回收,開發(fā)人員為什么要學習GC和內(nèi)存分配呢?為了能夠配置上面的參數(shù)配置?參數(shù)配置又是為了什么?
“當需要排查各種內(nèi)存溢出,內(nèi)存泄露問題時,當垃圾成為系統(tǒng)達到更高并發(fā)量的瓶頸時,我們就需要對GC的自動回收實施必要的監(jiān)控和調(diào)節(jié)。”
JVM中程序計數(shù)器、虛擬機棧、本地方法棧3個區(qū)域隨線程而生隨線程而滅。棧幀隨著方法的進入和退出做入棧和出棧操作,實現(xiàn)了自動的內(nèi)存清理。它們的內(nèi)存分配和回收都具有確定性。
因此,GC垃圾回收主要集中在堆和方法區(qū),在程序運行期間,這部分內(nèi)存的分配和使用都是動態(tài)的。
下面通過概念和具體的算法來了解GC垃圾回收的過程。
判斷對象常規(guī)有兩種方法:引用計數(shù)算法和可達性分析算法(Reachability Analysis)。
引用計數(shù)算法:給對象添加一個引用計數(shù)器,每當有一個地方引用它時計數(shù)器加1,引用釋放時計數(shù)減1,當計數(shù)器為0時可以回收。
引用計數(shù)算法實現(xiàn)簡單,判斷高效,在微軟COM和Python語言等被廣泛使用,但在主流的Java虛擬機中沒有使用該方法,主要是因為無法解決對象相互循環(huán)引用的問題。
可達性分析算法:基本思想是通過一系列稱為“GC Root”的對象(如系統(tǒng)類加載器、棧中的對象、處于激活狀態(tài)的線程等)作為起點,基于對象引用關(guān)系,開始向下搜索,所走過的路徑稱為引用鏈,當一個對象到GC Root沒有任何引用鏈相連,證明對象是不可用的。
上圖中中綠色部分為存活對象,灰色部分為可回收對象。雖然灰色部分內(nèi)部依舊有關(guān)聯(lián),但它們到GC Root是不可達的。
面試官,說說Java GC都用了哪些算法?分別應用在什么地方?
答:復制算法、標記清除、標記整理……
你還在單純的死記硬背么?繼續(xù)往下看,你會豁然開朗,再也不用死記硬背了。
標記清除(Mark-Sweep)算法,包含“標記”和“清除”兩個階段:首先標記出所有需要回收的對象,在標記完成后統(tǒng)一回收掉所有被標記的對象。
標記清除算法是最基礎的收集算法,后續(xù)的收集算法都是基于該思路并對其缺點進行改進而得到的。
主要缺點:一個是效率問題,標記和清除過程的效率都不高;另外是空間問題,標記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導致,當程序在以后的運行過程中需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作。
復制(Copying)算法:將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當一塊內(nèi)存用完了,就將還存活著的對象復制到另外一塊上,然后清理掉前一塊。
每次對半?yún)^(qū)內(nèi)存回收時、內(nèi)存分配時就不用考慮內(nèi)存碎片等復雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可,實現(xiàn)簡單,運行高效。
缺點:將內(nèi)存縮小為一半,性價比低,持續(xù)復制長生存期的對象則導致效率低下。
JVM堆中新生代便采用復制算法。回到最初推分配結(jié)構(gòu)圖。
在GC回收過程中,當Eden區(qū)滿時,還存活的對象會被復制到其中一個Survivor區(qū);當回收時,會將Eden和使用的Survivor區(qū)還存活的對象,復制到另外一個Survivor區(qū),然后對Eden和用過的Survivor區(qū)進行清理。
如果另外一個Survivor區(qū)沒有足夠的內(nèi)存存儲時,則會進入老年代。
這里針對哪些對象會進入老年代有這樣的機制:對象每經(jīng)歷一次復制,年齡加1,達到晉升年齡閾值后,轉(zhuǎn)移到老年代。
在這整個過程中,由于Eden中的對象屬于像浮萍一樣“瞬生瞬滅”的對象,所以并不需要1:1的比例來分配內(nèi)存,而是采用了8:1:1的比例來分配。
而針對那些像“水熊蟲”一樣,歷經(jīng)多次清理依舊存活的對象,則會進入老年代,而老年的清理算法則采用下面要講到的“標記整理算法”。
標記整理(Mark-Compact)算法:標記過程與“標記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存。
這種算法不既不用浪費50%的內(nèi)存,也解決了復制算法在對象存活率較高時的效率低下問題。
分代收集算法,基本思路:將Java的堆內(nèi)存邏輯上分成兩塊,新生代和老年代,針對不同存活周期、不同大小的對象采取不同的垃圾回收策略。
而在新生代中大多數(shù)對象都是瞬間對象,只有少量對象存活,復制較少對象即可完成清理,因此采用復制算法。而針對老年代中的對象,存活率較高,又沒有額外的擔保內(nèi)存,因此采用標記整理算法。
其實,回頭看,分代收集算法就是對新生代和老年代算法從策略維度的規(guī)劃而已。
至此,當面試官再問Java GC都用到了哪些垃圾回收算法和分別應用在什么場景下的問題,再也不用死記硬背了吧?
當前文章:面試官,不要再問我“JavaGC垃圾回收機制”了
轉(zhuǎn)載來源:http://jinyejixie.com/article44/gdpcee.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、定制開發(fā)、網(wǎng)站內(nèi)鏈、企業(yè)建站、微信小程序、網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)