经典面试题:聊一聊垃圾回收算法
關(guān)于垃圾回收算法的這道面試題,幾乎是所有 3 年以上的 Java 面試中必問的題目,甚至有些好一點的公司會在校招時問到面試者。那么本文就系統(tǒng)的講一下垃圾回收的算法,和 Hotspot 虛擬機執(zhí)行垃圾回收的一些實現(xiàn)細節(jié),比如安全點和安全區(qū)域等。
因為各個平臺的虛擬機操作內(nèi)存的方法各不相同,且牽扯大量的程序?qū)崿F(xiàn)細節(jié),所以本文不會過多的討論算法的具體實現(xiàn),只會介紹幾種算法思想及發(fā)展過程。
垃圾回收算法
1、標記-清除算法
標記-清除算法是最基礎(chǔ)的算法,像它的名字一樣算法分為“標記”和“清除”兩個階段,首先需要標記出所需要回收的對象,標記完成后統(tǒng)一收集被標記的對象。
優(yōu)點: 實現(xiàn)簡單。
缺點: 產(chǎn)生不連續(xù)的內(nèi)存碎片;“標記”和“清除”的執(zhí)行效率都不高。
標記-清除算法執(zhí)行過程圖:
(本文圖片來自《深入理解Java虛擬機》)
2、復(fù)制算法
復(fù)制算法就是將內(nèi)存分為大小相同的兩塊,當這一塊使用完了,就把當前存活的對象復(fù)制到另一塊,然后一次性清空當前區(qū)塊。
優(yōu)點: 執(zhí)行效率高。
缺點: 空間利用率低, 因為復(fù)制算法每次只能使用一半的內(nèi)存。
3、標記-整理算法
也稱標記-壓縮算法,標記-整理算法采用和標記清除算法一樣的對象“標記”,但后續(xù)不會對可回收對象進行清理,而是將存活的對象往一端空閑空間移動,然后清理邊界以外的內(nèi)存空間。
優(yōu)點: 解決了內(nèi)存碎片問題,比復(fù)制算法空間利用率高。
缺點: 因為有局部對象移動,相對效率不高。
標記-整理算法執(zhí)行過程圖:
4、分代收集算法
目前商用虛擬機都采用的是分代收集的算法,這種算法按照對象存活周期把內(nèi)存分為幾塊,一般Java中分為新生代和老年代。把存活率低的對象分到新生代使用復(fù)制算法提高垃圾回收的性能,老年代則存放存活率搞的對象,使用標記-清除和標記-整理的算法,提高內(nèi)存空間使用率。
新生代和老生代的具體介紹和參數(shù)配置,后續(xù)的文章會詳細講解。
垃圾回收執(zhí)行細節(jié)
本節(jié)將詳細的介紹一下HotSpot虛擬機在執(zhí)行垃圾回收時的一些細節(jié),目的是讓讀者更好的理解Java虛擬機。
HotSpot虛擬機: 它是Sun JDK和OpenJDK自定的虛擬機,也是目前使用最廣泛的虛擬機。
垃圾回收流程: Java虛擬機在內(nèi)存回收之前,為了保證內(nèi)存的一致性,必須先暫停程序的執(zhí)行,也就是傳說中的Stop The World(簡稱STW),在使用可達性分析算法枚舉GC Roots,標記出死亡對象,再進行垃圾回收。
垃圾回收遇到的問題: 那既然是要暫停程序的運行,就一定要保證停止的時間足夠短,并且可控,不然帶來的災(zāi)難將是毀滅性的。
解決方案: 顯然HotSpot在設(shè)計的時候也考慮到了這個問題,所以在JIT編譯的時候就會使用OopMap數(shù)據(jù)結(jié)構(gòu)來記錄棧和寄存器上的引用,這樣虛擬機就直接知道了那些地方存放著對象的引用,如下圖,為我編譯String.hashCode()方法的部分本地代碼:
可以看出,使用OopMap數(shù)據(jù)結(jié)構(gòu)存儲了普通對象的指針引用。
查看匯編的方法,啟動命令窗體執(zhí)行: java-XX:+UnlockDiagnosticVMOptions-XX:+PrintAssemblyYouTestClass
命令可能會報錯: Couldnotload hsdis-amd64.dll;librarynotloadable;PrintAssemblyisdisabled
報錯解決方法:使用編譯好的hsdis.dll放到:jre安裝目錄\bin\server目錄下即可,hsdis.dll地址地址:https://pan.baidu.com/s/1-D6u0gnUx291LXS3bHOorA
安全點(Safepoint)
在OopMap的協(xié)助下,HotSpot可以快速的完成GC Roots枚舉,但導(dǎo)致OopMap內(nèi)容變化的指令很多,而且如果給每個對象生成對應(yīng)的OopMap,會造成大量額外的空間,這會導(dǎo)致GC成本很高,所以HotSpot只會在“特定的位置”生成對應(yīng)的OopMap,這些位置就成為“安全點”。
HotSpot也并不是任何時刻都會停頓下來進行GC,只會在程序都到底安全點之后才會GC,所以安全點的設(shè)置不能太少,讓GC等待時間太長,也不能太多增大運行時的成本。
安全點的兩種線程中斷方式
搶斷式中斷:不需要線程的執(zhí)行代碼去主動配合,當發(fā)生GC時,先強制中斷所有線程,然后如果發(fā)現(xiàn)某些線程未處于安全點,恢復(fù)程序運行,直到進入安全點為止。
主動式中斷:不強制中斷線程,只是簡單地設(shè)置一個中斷標記,各個線程在執(zhí)行時輪詢這個標記,一旦發(fā)現(xiàn)標記被改變(出現(xiàn)中斷標記)時,那么將運行到安全點后自己中斷掛起。目前所有商用虛擬機全部采用主動式中斷。
安全區(qū)域(Saferegion)
安全點機制僅僅是保證了程序執(zhí)行時不需要太長時間就可以進入一個安全點進行 GC 動作,但是當特殊情況時,比如線程休眠、線程阻塞等狀態(tài)的情況下,顯然HotSpot不可能一直等待被阻塞或休眠的線程正常喚醒執(zhí)行;此時就引入了安全區(qū)的概念。
安全區(qū)(Saferegion):安全區(qū)域是指在一段區(qū)域內(nèi),對象引用關(guān)系等不會發(fā)生變化,在此區(qū)域內(nèi)任意位置開始GC都是安全的;線程運行時,首先標記自己進入了安全區(qū),然后在這段區(qū)域內(nèi),如果線程發(fā)生了阻塞、休眠等操作,HotSpot發(fā)起GC時將忽略這些處于安全區(qū)的線程。當線程再次被喚醒時,首先他會檢查是否完成了GC Roots枚舉(或這個GC過程),如果完成了就繼續(xù)執(zhí)行,否則將繼續(xù)等待直到收到可以安全離開的Safe Region的信號為止。
總結(jié)
前面講的垃圾回收器算法屬于基本功,要求面試者一定要熟練記憶的,而后面的垃圾收集器的執(zhí)行細節(jié)如果加分項,如果都能融會貫通的話,Offer 就離你又進一步了。
【End】
查看更多面試題內(nèi)容,請訪問《Java最常見200+面試題全解析》,它包含的模塊有:
Java、JVM?最常見面試題解析
Spring、Spring?MVC、MyBatis、Hibernate?面試題解析
MySQL、Redis?面試題解析
RabbitMQ、Kafka、Zookeeper?面試解析
微服務(wù)?Spring?Boot、Spring?Cloud?面試解析
掃描下面二維碼付費閱讀
關(guān)注下方二維碼,訂閱更多精彩內(nèi)容。
轉(zhuǎn)發(fā)朋友圈,是對我最大的支持。
總結(jié)
以上是生活随笔為你收集整理的经典面试题:聊一聊垃圾回收算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OkHttp透明压缩,收获性能10倍,外
- 下一篇: Redis 如何实现限流功能?