輕鬆理解JVM的分代模型

 

前言

上篇文章咱們一塊兒對jvm的內存模型有了比較清晰的認識,小夥伴們能夠參考JVM內存模型再也不是祕密這篇文章作一個複習。java

本篇文章咱們將針對jvm堆內存的分代模型作一個詳細的解析,和你們一塊兒輕鬆理解jvm的分代模型。算法

相信看過其餘文章的小夥伴們可能都知道,jvm的分代模型包括:年輕代、老年代、永久代。jvm

那麼它們分別表明着什麼角色呢?咱們先來看一段代碼ide

public class Main {    public static void main(String[] args) {        while (true){
            load();
        }
    }    public static void load(){
        SysUser sysUser = new SysUser();
        sysUser.setAvatar("1");
    }

}

這段代碼自己沒有什麼特殊的含義,主要是理解jvm的運行機制。學習

首先一旦執行main()方法,就會把main()方法的棧幀壓入main線程的虛擬機棧,而後調用load()方法後,又會把load()方法的棧幀壓入虛擬機棧。編碼

接着在執行load()方法時,會在java堆內存中建立一個SysUser對象實例,而棧幀中會有sysUser局部變量引用堆內存中的SysUser對象實例。線程

以下圖:3d

 

 到這裏上篇文章都講解過,相信你們都能看懂。對象

 

變量的存活時間

如今咱們思考一下會發現,這個SysUser對象實際上屬於一個短暫存活的對象,由於在load()方法執行完畢後,load()方法的棧幀就會出棧。blog

而一旦出棧,就沒有了sysUser這個局部變量來引用SysUser這個對象的實例。

因此,其實這個SysUser對象已經沒有用了,可是它還在佔用着堆內存的空間,那麼對於這種沒有引用的對象實例jvm是如何處理的呢?

這就要說到jvm的垃圾回收機制了,jvm自己是有垃圾回收機制的,它是一個後臺線程,會把沒有人引用的SysUser對象實例給回收掉,不斷的釋放內存空間。

因此這個SysUser對象實例是一個存活時間很短的對象,可能在執行load()方法的時候被建立出來,執行以後就被垃圾回收掉了。

而這種對象在咱們平時的開發中是很常見的,佔絕大多數比例。

 

如今咱們將上邊的代碼改造一下:

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");
    }
}

其實就是把局部變量sysUser變成了靜態變量,這樣修改後,sysUser不在做爲局部變量保存在棧中,而是和class類文件一塊兒保存在方法區中,這樣SysUser對象實例就會一直被這個靜態變量引用,因此不會被垃圾回收,一直保存在堆內存中。以下圖:

 

 

分代模型

接下來咱們進入核心內容,就是jvm的分代模型了。

上文中咱們發現,根據咱們的編碼方式的不一樣,採用不一樣的方式建立和使用對象,對象的存活時間是不一樣的。

因此jvm將內存區分爲兩個區域:年輕代和老年代。

年輕代就是咱們的第一種局部變量的示例,建立和使用完畢後會被垃圾回收掉。

老年代就是第二種靜態變量的示例,建立後須要長期在堆內存中存活。

相信到這裏你們就應該理解了什麼樣的對象是短時間存活的對象,什麼樣的對象是長期存活的對象,那麼它們是如何分別存在年輕代和老年代中的呢?爲何要這麼區分呢?

其實這與垃圾回收機制是密不可分的。

對於年輕代裏的對象,他們的特色是建立後很快就會被回收,而對於老年代裏的對象,他們的特色是須要長期存活,因此這兩種對象是不能用一種垃圾回收算法進行回收的,因此須要區分紅兩個。

對於長期存在的靜態變量sysUser,其實剛開始的時候也是在年輕代的,那它是何時進入老年代的呢?咱們下文會講解這個問題。

那永久代又是什麼呢?其實永久代就是咱們說的jvm的方法區,用於存放一下類信息的,這部分以後的文章涉及到會詳解,如今理解到這就能夠了。

 

新生代的垃圾回收

前文咱們瞭解了,當load方法執行完畢出棧後,裏面的局部變量sysUser就沒了,堆內存中的SysUser對象就沒有引用了,因此會被垃圾回收掉。

那麼問題來了,是沒有引用後就會當即發生垃圾回收,回收掉沒有被引用的對象實例嗎?

其實不是這樣的,垃圾回收是有觸發條件的。

有一個比較常見的場景是這樣的,假設咱們的代碼中建立了大量的對象,致使堆內存中囤積了大量的對象,而後這些對象如今都沒有人引用了。

這個時候,若是新生代預先分配的內存空間被佔滿了,那麼咱們的代碼此時要新建立一個對象的時候,發現新生代空間滿了,怎麼辦?

這個時候就會觸發一次新生代的垃圾回收,也稱爲「Minor GC」或"Young GC",它會嘗試把新生代中沒有人引用的對象給回收掉,釋放空間。

下圖表達了這一過程:

 

 

長期存活的對象何時進入老年代

接下來咱們談論一個話題,靜態變量引用的長期存活的對象是何時進入老年代的。

上文咱們瞭解到,新生代的對象會經歷一次次的垃圾回收,而被靜態變量引用的對象由於一直被引用,因此一直不會被回收,因此此時jvm就有了一條規定。

若是新生代中的對象,在經歷了15次垃圾回收後,依然堅挺的存活着,那就證實它是個"老年人"了,而後它會被轉移到老年代中。

老年代就是存放這些年齡比較大的對象的。

那麼老年代中的對象會被垃圾回收嗎?

答案是確定的,由於老年代裏的對象隨着代碼的運行,也是能夠再也不被任何人引用的,就須要垃圾回收了。

或者說,隨着愈來愈多的對象進入老年代,老年代的內存也會被佔滿,因此必定是要對老年代進行垃圾回收的。

咱們暫時不用考慮具體是怎麼回收的,這個內容在以後的文章中咱們會有詳細的解析。

 

總結

今天就給你們準備了這麼多內容,可能有些小夥伴以爲還沒看夠,這些內容都比較簡單,我已經會了,有沒有更深刻的東西呢?

別急,學習是按部就班的事情,王子是想要用最簡單的大白話來和小夥伴們一塊兒討論jvm的原理的,同時也想找一些案例來和你們一塊兒探討,印象會更深入。

因此今天小夥伴們瞭解到這裏就能夠了,讓咱們在後續的文章中不見不散,深刻討論些更深層的內容吧。

相關文章
相關標籤/搜索