009、大廠面試題:你的對象在JVM內(nèi)存中如何分配?如何流轉(zhuǎn)的?

大廠面試題:你的對象在JVM內(nèi)存中如何分配?如何流轉(zhuǎn)的?
目錄:
前文回顧
大部分正常對象都優(yōu)先在新生代分配內(nèi)存
到底什么情況下會觸發(fā)新生代的垃圾回收?
長期存活的對象會躲過多次垃圾回收
老年代會垃圾回收嗎?
關(guān)于新生代和老年代的對象分配,這就完了嗎?
昨日思考題解答
今日思考題
1、前文回顧
經(jīng)過昨天的文章鋪墊了一些對象分配的基礎(chǔ)知識后,想必大家現(xiàn)在都心里非常有數(shù)了,咱們平時(shí)代碼里創(chuàng)建出來的對象,一般就是兩種:
一種是短期存活的,分配在Java堆內(nèi)存之后,迅速使用完就會被垃圾回收
另外一種是長期存活的,需要一直生存在Java堆內(nèi)存里,讓程序后續(xù)不停的去使用
第一種短期存活的對象,是在Java堆內(nèi)存的新生代里的。第二種長期存活的對象,是在Java堆內(nèi)存的老年代里的。這個(gè)結(jié)論,想必大家都已經(jīng)理解了
好,那么接下來我們就來聊聊,對象到底什么時(shí)候進(jìn)入新生代?然后什么情況下會進(jìn)入老年代?
提示一下:本文是建立在大家都絕對理解上文的基礎(chǔ)上來寫的,上文是結(jié)合代碼示例來闡述的核心原理,包括對象什么情況下短期存活,什么情況下長期存活。
所以本文就直接通過大量圖示來給大家分析對象在內(nèi)存中的分配機(jī)制了,大家務(wù)必透徹理解上文。
2、大部分正常對象都優(yōu)先在新生代分配內(nèi)存
首先我們先來看上篇文章中的一段代碼,稍微帶著大家來理解一個(gè)概念:大部分的正常對象,都是優(yōu)先在新生代分配內(nèi)存的。

大家還記得上面那段代碼嗎?雖然我們看代碼知道,類靜態(tài)變量“fetcher”引用的那個(gè)“ReplicaFetcher”對象,是會長期存活在內(nèi)存里的
但是哪怕是這種對象,其實(shí)剛開始你通過“new ReplicaFetcher()”代碼來實(shí)例化一個(gè)對象時(shí),他也是分配在新生代里的。
包括在“l(fā)oadReplicasFromDisk()”方法中創(chuàng)建的“ReplicaManager”實(shí)例對象,也都是一樣分配在新生代里的
同樣,我們以一張圖,來展示一下:

3、到底什么情況下會觸發(fā)新生代的垃圾回收?
現(xiàn)在咱們來假設(shè)一個(gè)場景,大家應(yīng)該都知道,一旦“l(fā)oadReplicasFromDisk()”方法執(zhí)行完畢之后,這個(gè)方法的棧幀出棧,會導(dǎo)致沒有任何局部變量引用那個(gè)“ReplicaManager”實(shí)例對象了。
此時(shí)可能會如下圖所示:

那么此時(shí)就一定會立即發(fā)生垃圾回收,去回收掉Java堆內(nèi)存里那個(gè)沒人使用的“ReplicaManager”實(shí)例對象嗎?
NO!大家別想的那么簡單了,實(shí)際上垃圾回收他也得有點(diǎn)觸發(fā)的條件。
其中一個(gè)比較常見的場景可能是這樣的,假設(shè)我們寫的代碼中創(chuàng)建了N多對象,然后導(dǎo)致Java堆內(nèi)存里囤積了大量的對象。
然后這些對象都是之前有人引用,比如各種各樣的方法中的局部變量,但是現(xiàn)在也都沒人引用了。
如下圖所示:

這個(gè)時(shí)候,如果新生代我們預(yù)先分配的內(nèi)存空間,幾乎都被全部對象給占滿了!此時(shí)假設(shè)我們代碼繼續(xù)運(yùn)行,他需要在新生代里去分配一個(gè)對象,怎么辦?發(fā)現(xiàn)新生代里內(nèi)存空間都不夠了!
這個(gè)時(shí)候,就會觸發(fā)一次新生代內(nèi)存空間的垃圾回收,新生代內(nèi)存空間的垃圾回收,也稱之為“Minor GC”,有的時(shí)候我們也叫“Young GC”,他會嘗試把新生代里那些沒有人引用的垃圾對象,都給回收掉。
比如上圖中,那個(gè)“ReplicaManager”實(shí)例對象,其實(shí)就是沒有人引用的垃圾對象
此時(shí)就會當(dāng)機(jī)立斷,把“ReplicaManager”實(shí)例對象給回收掉,騰出更多的內(nèi)存空間,然后放一個(gè)新的對象到新生代里去。
包括上圖中那大量的實(shí)例對象,其實(shí)也都沒人引用,在這個(gè)新生代垃圾回收的過程中,就會把這些垃圾對象也都回收掉。
其實(shí)話說回來,大家自己仔細(xì)回憶一下,我們在代碼中創(chuàng)建的大部分對象,其實(shí)都是這種使用之后立馬就可以回收掉的生存周期極短的對象,是不是?
可能我們會在新生代里分配大量的對象,但是使用完之后立馬就沒人引用了,此時(shí)新生代差不多滿了
然后要分配新的對象的時(shí)候,發(fā)現(xiàn)新生代內(nèi)存空間不足,就會觸發(fā)一次垃圾回收,然后就把所有垃圾對象給干掉,騰出大量的內(nèi)存空間
如下圖所示:

4、長期存活的對象會躲過多次垃圾回收
接著我們來看下一個(gè)問題,上圖中大家都注意到了“ReplicaFetcher”實(shí)例對象,他是一個(gè)長期被“Kafka”類的靜態(tài)變量“fetcher”引用的長期存活的對象。
所以雖然你的新生代可能隨著系統(tǒng)的運(yùn)行,不停的創(chuàng)建對象,然后讓新生代變滿,接著垃圾回收一次,大量對象被回收掉
但是你的這個(gè)“ReplicaFetcher”對象,他確是一直會存活在新生代里的。
因?yàn)樗恢北弧癒afka”類的靜態(tài)變量給引用了,所以他不會被回收。那么此時(shí)JVM就有一條規(guī)定了
如果一個(gè)實(shí)例對象在新生代中,成功的在15次垃圾回收之后,還是沒被回收掉,就說明他已經(jīng)15歲了。
這是對象的年齡,每垃圾回收一次,如果一個(gè)對象沒被回收掉,他的年齡就會增加1。
所以如果上圖中的那個(gè)“ReplicaFetcher”對象在新生代中成功躲過10多次垃圾回收,成為一個(gè)“老年人”,那么就會被認(rèn)為是會長期存活在內(nèi)存里的對象。
然后他會被轉(zhuǎn)移到Java堆內(nèi)存的老年代中去,顧名思義,老年代就是放這些年齡很大的對象。
我們再來看一張圖:

5、老年代會垃圾回收嗎?
接著下一個(gè)問題就是,老年代里的那些對象會被垃圾回收嗎?
答案是肯定的,因?yàn)槔夏甏锏膶ο笠灿锌赡茈S著代碼的運(yùn)行,不再被任何人引用了,就需要被垃圾回收。
大家可以思考一下,如果隨著類似上面的情況,越來越多的對象進(jìn)入老年代,一旦老年代也滿了,是不是就要對老年代垃圾回收了?
沒錯(cuò),這是肯定的,但是暫時(shí)我們先不用過多的去考慮這里的細(xì)節(jié),因?yàn)檫@將是下周的主題,下周我們會進(jìn)行深入剖析。
6、關(guān)于新生代和老年代的對象分配,這就完了嗎?
還有人會說,關(guān)于新生代和老年代的對象分配,這就結(jié)束了嗎?
當(dāng)然不是,今天這篇文章,僅僅是相較于之前的文章,更進(jìn)一步給大家分析了一下對象分配的一些機(jī)制。
但是其實(shí)在對象分配這塊,還有很多其他的復(fù)雜機(jī)制,比如:
新生代垃圾回收之后,因?yàn)榇婊顚ο筇啵瑢?dǎo)致大量對象直接進(jìn)入老年代
特別大的超大對象直接不經(jīng)過新生代就進(jìn)入老年代
動態(tài)對象年齡判斷機(jī)制
空間擔(dān)保機(jī)制
可能一些JVM書籍會在這里一下把這些復(fù)雜的東西都寫出來給大家,但是我們的專欄不會是這個(gè)思路。
還是那句話,我們的專欄寫作思路是循序漸進(jìn),從淺入深,通俗易懂,一步一圖。
很多底層技術(shù)細(xì)節(jié),不要在前期鋪墊太多,會導(dǎo)致很多同學(xué)吃了沒法消化
我會結(jié)合后續(xù)大量案例,結(jié)合真實(shí)生產(chǎn)問題,把JVM各種底層細(xì)節(jié)帶出來。結(jié)合實(shí)戰(zhàn)食用,效果更佳。
因此第二周,大家對對象內(nèi)存分配,了解到這個(gè)程度就行了,給大家總結(jié)一下:
先理解對象優(yōu)先分配在新生代
新生代如果對象滿了,會觸發(fā)Minor GC回收掉沒有人引用的垃圾對象
如果有對象躲過了十多次垃圾回收,就會放入老年代里
如果老年代也滿了,那么也會觸發(fā)垃圾回收,把老年代里沒人引用的垃圾對象清理掉
大家通過本文,先理解上面幾點(diǎn)即可。
7、昨日思考題解答
昨天的思考題,是一個(gè)腦筋急轉(zhuǎn)彎,說每個(gè)線程執(zhí)行方法的時(shí)候,那些方法對應(yīng)的棧幀出棧了,那么那里的局部變量需要垃圾回收嗎?
其實(shí)這是一個(gè)偏題,JVM里垃圾回收針對的是新生代,老年代,還有方法區(qū)(永久代),不會針對方法的棧幀。
方法一旦執(zhí)行完畢,棧幀出棧,里面的局部變量直接就從內(nèi)存里清理掉了。
8、今日思考題
今天想給大家出一個(gè)預(yù)習(xí)類的思考題:理解了今天的對象內(nèi)存分配,垃圾回收以及老年代轉(zhuǎn)移的機(jī)制之后。
大家能否結(jié)合短生存周期的對象的特點(diǎn),以及長生存周期的對象的特點(diǎn),思考一下,看看你們手頭正在負(fù)責(zé)的系統(tǒng),梳理梳理里面短生存周期的對象都有什么,長生存周期的對象都有什么。
可以在評論區(qū)踴躍回復(fù),讓大家開始從JVM的角度去思考自己手頭負(fù)責(zé)的系統(tǒng)中的代碼是怎么運(yùn)行的。
End
版權(quán):公眾號儒猿技術(shù)窩
未經(jīng)許可不得傳播,如有侵權(quán)將追究法律責(zé)任