詳解JVM的分代模型
前言
上篇文章我們一起對(duì)jvm的內(nèi)存模型有了比較清晰的認(rèn)識(shí),小伙伴們可以參考JVM內(nèi)存模型不再是秘密這篇文章做一個(gè)復(fù)習(xí)。
本篇文章我們將針對(duì)jvm堆內(nèi)存的分代模型做一個(gè)詳細(xì)的解析,和大家一起輕松理解jvm的分代模型。
相信看過(guò)其他文章的小伙伴們可能都知道,jvm的分代模型包括:年輕代、老年代、永久代。
那么它們分別代表著什么角色呢?我們先來(lái)看一段代碼
public class Main { public static void main(String[] args) { while (true){ load(); } } public static void load(){ SysUser sysUser = new SysUser(); sysUser.setAvatar("1"); } }
這段代碼本身沒(méi)有什么特殊的含義,主要是理解jvm的運(yùn)行機(jī)制。
首先一旦執(zhí)行main()方法,就會(huì)把main()方法的棧幀壓入main線程的虛擬機(jī)棧,然后調(diào)用load()方法后,又會(huì)把load()方法的棧幀壓入虛擬機(jī)棧。
接著在執(zhí)行l(wèi)oad()方法時(shí),會(huì)在java堆內(nèi)存中創(chuàng)建一個(gè)SysUser對(duì)象實(shí)例,而棧幀中會(huì)有sysUser局部變量引用堆內(nèi)存中的SysUser對(duì)象實(shí)例。
如下圖:
到這里上篇文章都講解過(guò),相信大家都能看懂。
變量的存活時(shí)間
現(xiàn)在我們思考一下會(huì)發(fā)現(xiàn),這個(gè)SysUser對(duì)象實(shí)際上屬于一個(gè)短暫存活的對(duì)象,因?yàn)樵趌oad()方法執(zhí)行完畢后,load()方法的棧幀就會(huì)出棧。
而一旦出棧,就沒(méi)有了sysUser這個(gè)局部變量來(lái)引用SysUser這個(gè)對(duì)象的實(shí)例。
所以,其實(shí)這個(gè)SysUser對(duì)象已經(jīng)沒(méi)有用了,但是它還在占用著堆內(nèi)存的空間,那么對(duì)于這種沒(méi)有引用的對(duì)象實(shí)例jvm是如何處理的呢?
這就要說(shuō)到j(luò)vm的垃圾回收機(jī)制了,jvm本身是有垃圾回收機(jī)制的,它是一個(gè)后臺(tái)線程,會(huì)把沒(méi)有人引用的SysUser對(duì)象實(shí)例給回收掉,不斷的釋放內(nèi)存空間。
所以這個(gè)SysUser對(duì)象實(shí)例是一個(gè)存活時(shí)間很短的對(duì)象,可能在執(zhí)行l(wèi)oad()方法的時(shí)候被創(chuàng)建出來(lái),執(zhí)行之后就被垃圾回收掉了。
而這種對(duì)象在我們平時(shí)的開(kāi)發(fā)中是很常見(jiàn)的,占絕大多數(shù)比例。
現(xiàn)在我們將上邊的代碼改造一下:
public class Main { private static SysUser sysUser = new SysUser(); public static void main(String[] args) { while (true){ load(); } } public static void load(){ sysUser.setAvatar("1"); } }
其實(shí)就是把局部變量sysUser變成了靜態(tài)變量,這樣修改后,sysUser不在作為局部變量保存在棧中,而是和class類文件一起保存在方法區(qū)中,這樣SysUser對(duì)象實(shí)例就會(huì)一直被這個(gè)靜態(tài)變量引用,所以不會(huì)被垃圾回收,一直保存在堆內(nèi)存中。如下圖:
分代模型
接下來(lái)我們進(jìn)入核心內(nèi)容,就是jvm的分代模型了。
上文中我們發(fā)現(xiàn),根據(jù)我們的編碼方式的不同,采用不同的方式創(chuàng)建和使用對(duì)象,對(duì)象的存活時(shí)間是不同的。
所以jvm將內(nèi)存區(qū)分為兩個(gè)區(qū)域:年輕代和老年代。
年輕代就是我們的第一種局部變量的示例,創(chuàng)建和使用完畢后會(huì)被垃圾回收掉。
老年代就是第二種靜態(tài)變量的示例,創(chuàng)建后需要長(zhǎng)期在堆內(nèi)存中存活。
相信到這里大家就應(yīng)該理解了什么樣的對(duì)象是短期存活的對(duì)象,什么樣的對(duì)象是長(zhǎng)期存活的對(duì)象,那么它們是如何分別存在年輕代和老年代中的呢?為什么要這么區(qū)分呢?
其實(shí)這與垃圾回收機(jī)制是密不可分的。
對(duì)于年輕代里的對(duì)象,他們的特點(diǎn)是創(chuàng)建后很快就會(huì)被回收,而對(duì)于老年代里的對(duì)象,他們的特點(diǎn)是需要長(zhǎng)期存活,所以這兩種對(duì)象是不能用一種垃圾回收算法進(jìn)行回收的,所以需要區(qū)分成兩個(gè)。
對(duì)于長(zhǎng)期存在的靜態(tài)變量sysUser,其實(shí)剛開(kāi)始的時(shí)候也是在年輕代的,那它是什么時(shí)候進(jìn)入老年代的呢?我們下文會(huì)講解這個(gè)問(wèn)題。
那永久代又是什么呢?其實(shí)永久代就是我們說(shuō)的jvm的方法區(qū),用于存放一下類信息的,這部分之后的文章涉及到會(huì)詳解,現(xiàn)在理解到這就可以了。
新生代的垃圾回收
前文我們了解了,當(dāng)load方法執(zhí)行完畢出棧后,里面的局部變量sysUser就沒(méi)了,堆內(nèi)存中的SysUser對(duì)象就沒(méi)有引用了,所以會(huì)被垃圾回收掉。
那么問(wèn)題來(lái)了,是沒(méi)有引用后就會(huì)立即發(fā)生垃圾回收,回收掉沒(méi)有被引用的對(duì)象實(shí)例嗎?
其實(shí)不是這樣的,垃圾回收是有觸發(fā)條件的。
有一個(gè)比較常見(jiàn)的場(chǎng)景是這樣的,假設(shè)我們的代碼中創(chuàng)建了大量的對(duì)象,導(dǎo)致堆內(nèi)存中囤積了大量的對(duì)象,然后這些對(duì)象現(xiàn)在都沒(méi)有人引用了。
這個(gè)時(shí)候,如果新生代預(yù)先分配的內(nèi)存空間被占滿了,那么我們的代碼此時(shí)要新創(chuàng)建一個(gè)對(duì)象的時(shí)候,發(fā)現(xiàn)新生代空間滿了,怎么辦?
這個(gè)時(shí)候就會(huì)觸發(fā)一次新生代的垃圾回收,也稱為“Minor GC”或"Young GC",它會(huì)嘗試把新生代中沒(méi)有人引用的對(duì)象給回收掉,釋放空間。
下圖表達(dá)了這一過(guò)程:
長(zhǎng)期存活的對(duì)象什么時(shí)候進(jìn)入老年代
接下來(lái)我們談?wù)撘粋€(gè)話題,靜態(tài)變量引用的長(zhǎng)期存活的對(duì)象是什么時(shí)候進(jìn)入老年代的。
上文我們了解到,新生代的對(duì)象會(huì)經(jīng)歷一次次的垃圾回收,而被靜態(tài)變量引用的對(duì)象因?yàn)橐恢北灰?,所以一直不?huì)被回收,所以此時(shí)jvm就有了一條規(guī)定。
如果新生代中的對(duì)象,在經(jīng)歷了15次垃圾回收后,依然堅(jiān)挺的存活著,那就證明它是個(gè)"老年人"了,然后它會(huì)被轉(zhuǎn)移到老年代中。
老年代就是存放這些年齡比較大的對(duì)象的。
那么老年代中的對(duì)象會(huì)被垃圾回收嗎?
答案是肯定的,因?yàn)槔夏甏锏膶?duì)象隨著代碼的運(yùn)行,也是可以不再被任何人引用的,就需要垃圾回收了。
或者說(shuō),隨著越來(lái)越多的對(duì)象進(jìn)入老年代,老年代的內(nèi)存也會(huì)被占滿,所以一定是要對(duì)老年代進(jìn)行垃圾回收的。
我們暫時(shí)不用考慮具體是怎么回收的,這個(gè)內(nèi)容在之后的文章中我們會(huì)有詳細(xì)的解析。
總結(jié)
今天就給大家準(zhǔn)備了這么多內(nèi)容,可能有些小伙伴覺(jué)得還沒(méi)看夠,這些內(nèi)容都比較簡(jiǎn)單,我已經(jīng)會(huì)了,有沒(méi)有更深入的東西呢?
別急,學(xué)習(xí)是循序漸進(jìn)的事情,王子是想要用最簡(jiǎn)單的大白話來(lái)和小伙伴們一起討論jvm的原理的,同時(shí)也想找一些案例來(lái)和大家一起探討,印象會(huì)更深刻。
所以今天小伙伴們了解到這里就可以了,讓我們?cè)诤罄m(xù)的文章中不見(jiàn)不散,深入討論些更深層的內(nèi)容吧。
以上就是詳解JVM的分代模型的詳細(xì)內(nèi)容,更多關(guān)于JVM 分代模型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring實(shí)戰(zhàn)之Qualifier注解用法示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之Qualifier注解用法,結(jié)合實(shí)例形式詳細(xì)分析了spring Qualifier注解相關(guān)配置、定義與使用方法,需要的朋友可以參考下2019-12-12springboot 使用yml配置文件給靜態(tài)變量賦值教程
這篇文章主要介紹了springboot 使用yml配置文件給靜態(tài)變量賦值教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-04-04java正則表達(dá)式校驗(yàn)日期格式實(shí)例代碼
如果使用得當(dāng),正則表達(dá)式是匹配各種模式的強(qiáng)大工具,下面這篇文章主要給大家介紹了關(guān)于java正則表達(dá)式校驗(yàn)日期格式的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-05-05深入淺出重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)
通常來(lái)講,重構(gòu)是指不改變功能的情況下優(yōu)化代碼,但本文所說(shuō)的重構(gòu)也包括了添加功能。這篇文章主要介紹了重構(gòu)Mybatis與Spring集成的SqlSessionFactoryBean(上)的相關(guān)資料,需要的朋友可以參考下2016-11-11