jvm性能调优 - 05对象在JVM内存中的分配和流转
文章目錄
- 前文回顧
- 大部分正常對象都優先在新生代分配內存
- 到底什么情況下會觸發新生代的垃圾回收?
- 長期存活的對象會躲過多次垃圾回收?
- 老年代會垃圾回收嗎?
- 關于新生代和老年代的對象分配,這就完了嗎?
前文回顧
經過上一篇文章鋪墊了一些對象分配的基礎知識后,想必大家現在都心里非常有數了,咱們平時代碼里創建出來的對象,一般就是兩種:
-
一種是短期存活的,分配在Java堆內存之后,迅速使用完就會被垃圾回收
-
另外一種是長期存活的,需要一直生存在Java堆內存里,讓程序后續不停的去使用
第一種短期存活的對象,是在Java堆內存的新生代里的。第二種長期存活的對象,是在Java堆內存的老年代里的。這個結論,想必大家都已經理解了
好,那么接下來我們就來聊聊,對象到底什么時候進入新生代?然后什么情況下會進入老年代?
大部分正常對象都優先在新生代分配內存
首先我們先來看上篇文章中的一段代碼,稍微帶著大家來理解一個概念:大部分的正常對象,都是優先在新生代分配內存的。
雖然我們看代碼知道,類靜態變量“fetcher”引用的那個“ReplicaFetcher”對象,是會長期存活在內存里的
但是哪怕是這種對象,其實剛開始你通過“new ReplicaFetcher()”代碼來實例化一個對象時,他也是分配在新生代里的。
包括在“loadReplicasFromDisk()”方法中創建的“ReplicaManager”實例對象,也都是一樣分配在新生代里的
同樣,我們以一張圖,來展示一下:
到底什么情況下會觸發新生代的垃圾回收?
現在咱們來假設一個場景,大家應該都知道,一旦“loadReplicasFromDisk()”方法執行完畢之后,這個方法的棧幀出棧,會導致沒有任何局部變量引用那個“ReplicaManager”實例對象了。
此時可能會如下圖所示:
那么此時就一定會立即發生垃圾回收,去回收掉Java堆內存里那個沒人使用的“ReplicaManager”實例對象嗎?
NO!
大家別想的那么簡單了,實際上垃圾回收他也得有點觸發的條件。
其中一個比較常見的場景可能是這樣的,假設我們寫的代碼中創建了N多對象,然后導致Java堆內存里囤積了大量的對象。
然后這些對象都是之前有人引用,比如各種各樣的方法中的局部變量,但是現在也都沒人引用了。
如下圖所示
這個時候,如果新生代我們預先分配的內存空間,幾乎都被全部對象給占滿了!此時假設我們代碼繼續運行,他需要在新生代里去分配一個對象,怎么辦?發現新生代里內存空間都不夠了!
這個時候,就會觸發一次新生代內存空間的垃圾回收,新生代內存空間的垃圾回收,也稱之為“Minor GC”,有的時候我們也叫“Young GC”,他會嘗試把新生代里那些沒有人引用的垃圾對象,都給回收掉。
比如上圖中,那個“ReplicaManager”實例對象,其實就是沒有人引用的垃圾對象
此時就會當機立斷,把“ReplicaManager”實例對象給回收掉,騰出更多的內存空間,然后放一個新的對象到新生代里去。
包括上圖中那大量的實例對象,其實也都沒人引用,在這個新生代垃圾回收的過程中,就會把這些垃圾對象也都回收掉。
其實話說回來,大家自己仔細回憶一下,我們在代碼中創建的大部分對象,其實都是這種使用之后立馬就可以回收掉的生存周期極短的對象,是不是?
可能我們會在新生代里分配大量的對象,但是使用完之后立馬就沒人引用了,此時新生代差不多滿了
然后要分配新的對象的時候,發現新生代內存空間不足,就會觸發一次垃圾回收,然后就把所有垃圾對象給干掉,騰出大量的內存空間
如下圖所示:
長期存活的對象會躲過多次垃圾回收?
接著我們來看下一個問題,上圖中大家都注意到了“ReplicaFetcher”實例對象,他是一個長期被“Kafka”類的靜態變量“fetcher”引用的長期存活的對象。
所以雖然你的新生代可能隨著系統的運行,不停的創建對象,然后讓新生代變滿,接著垃圾回收一次,大量對象被回收掉
但是你的這個“ReplicaFetcher”對象,他確是一直會存活在新生代里的。
因為他一直被“Kafka”類的靜態變量給引用了,所以他不會被回收。那么此時JVM就有一條規定了
如果一個實例對象在新生代中,成功的在15次垃圾回收之后,還是沒被回收掉,就說明他已經15歲了。
這是對象的年齡,每垃圾回收一次,如果一個對象沒被回收掉,他的年齡就會增加1。
所以如果上圖中的那個“ReplicaFetcher”對象在新生代中成功躲過10多次垃圾回收,成為一個“老年人”,那么就會被認為是會長期存活在內存里的對象。
然后他會被轉移到Java堆內存的老年代中去,顧名思義,老年代就是放這些年齡很大的對象。
我們再來看一張圖:
老年代會垃圾回收嗎?
接著下一個問題就是,老年代里的那些對象會被垃圾回收嗎?
答案是肯定的,因為老年代里的對象也有可能隨著代碼的運行,不再被任何人引用了,就需要被垃圾回收。
大家可以思考一下,如果隨著類似上面的情況,越來越多的對象進入老年代,一旦老年代也滿了,是不是就要對老年代垃圾回收了?
沒錯,這是肯定的,但是暫時我們先不用過多的去考慮這里的細節,后面我們會進行深入剖析。
關于新生代和老年代的對象分配,這就完了嗎?
還有人會說,關于新生代和老年代的對象分配,這就結束了嗎?
當然不是,我們這里僅僅是相較于之前的文章,更進一步給大家分析了一下對象分配的一些機制。
但是其實在對象分配這塊,還有很多其他的復雜機制,比如:
-
新生代垃圾回收之后,因為存活對象太多,導致大量對象直接進入老年代
-
特別大的超大對象直接不經過新生代就進入老年代
-
動態對象年齡判斷機制
-
空間擔保機制
-
- …
這部分內容結合案例,結合真實生產問題,把JVM各種底層細節帶出來。
到這里 ,大家對對象內存分配,了解到這個程度就行了,給大家總結一下:
-
先理解對象優先分配在新生代
-
新生代如果對象滿了,會觸發Minor GC回收掉沒有人引用的垃圾對象
-
如果有對象躲過了十多次垃圾回收,就會放入老年代里
-
如果老年代也滿了,那么也會觸發垃圾回收,把老年代里沒人引用的垃圾對象清理掉
總結
以上是生活随笔為你收集整理的jvm性能调优 - 05对象在JVM内存中的分配和流转的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jvm性能调优 - 03垃圾回收机制
- 下一篇: jvm性能调优 - 06线上应用部署JV