詳解JVM的分代模型

2022-10-06 02:45:08 字數 2708 閱讀 5902

前言

本篇文章我們將針對jvm堆記憶體的分代模型做乙個詳細的解析,和大家一起輕鬆理解jvm的分代模型。

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

那麼它們分別代表著什麼角色呢?我們先來看一段**

public class main

} public static void load()

}這段**本身沒有什麼特殊的含義,主要是理解jvm的執行機制。

首先一旦執行main()方法,就會把main()方法的棧幀壓入main執行緒的虛擬機器棧,然後呼叫load()方法後,又會把load()方法的棧幀壓入虛擬機器棧。

接著在執行load()方法時,會在j**a堆記憶體中建立乙個sysuser物件例項,而棧幀中會有sysuser區域性變數引用堆記憶體中的sysuser物件例項。www.cppcns.com

如下圖:

到這裡上篇文章都講解過,相信大家都能看懂。

變數的存活時間

現在我們思考一下會發現,這個sysuser物件實際上屬於乙個短暫存活的物件,因為在load()方法執行完畢後,load()方法的棧幀就會出棧。

而一旦出棧,就沒有了sysuser這個區域性變數來引用sysuser這個物件的例項。

所以,其實這個sysuser物件已經沒有用了,但是它還在占用著堆記憶體的空間,那麼對於這種沒有引用的物件例項jvm是如何處理的呢?

這就要說到jvm的垃圾**機制了,jvm本身是有垃圾**機制的,它是乙個後台執行緒,會把沒有人引用的sysuser物件例項給**掉,不斷的釋放記憶體空間。

所以這個sysuser物件例項是乙個存活時間很短的物件,可能在執行load()方法的時候被建立出來,執行之後就被垃圾**掉了。

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

現在我們將上邊的**改造一下:

public class main

} public static void load()

}其實就是把區域性變數sysuser變成了靜態變數,這樣修改後,sysuser不在作為區域性變數儲存在棧中,而是和class類檔案一起儲存在方法區中,這樣sysuser物件例項就會一直被這個靜態變數引用,所以不會被垃圾**,一直儲存在堆記憶體中。如下圖:

分代模型

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

上文中我們發現,根據我們的編碼方式的不同,採用不同的方式建立和使用物件,物件的存活時間是不同的。

所以jvm將記憶體區分為兩個區域:年輕代和老年代。

年輕代就是我們的第一種區域性變數的示例,建立和使用完畢後會被垃圾**掉。

老年代就是第二種靜態變數的示例,建立後需要長期在堆記憶體中存活。

相信到這裡大家就應該理解了什麼樣的物件是短期存活的物件,什麼樣的物件是長期存活的物件,那麼它們是如何分別存在年輕代和老年代中的呢?為什麼要這麼區分呢?

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

對於年輕代里的物件,他們的特點是建立後很快就會被**,而對於老年代裡的物件,他們的特點是需要長期存活,所以這兩種物件是不能用一種垃圾**演算法進行**的,所以需要區分成兩個。

對於長期存在的靜態變數sysuse程式設計客棧r,其實剛開始的時候也是在年輕代的,那它是什麼時候進入老年代的呢?我們下文會講解這個問題。

那永久代又是什麼呢?其實永久代就是我們說的jvm的方法區,用於存放一下類資訊的,這部分之後的文章涉及到會詳解,現在理解到這就可以了。

新生代的垃圾**

前文我們了解了,當load方法執行完畢出棧後,裡面的區域性變數sysuser就沒了,堆記憶體中的sysuser物件就沒有引用了,所以會被垃圾**掉。

那麼問題來了,是沒有引用後就會立即發生垃圾**,**掉沒有被引用的物件例項嗎?

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

有乙個比較常見的場景是這樣的,假設我們的**中建立了大量的物件,導致堆記憶體中囤積了大量的物件,然後這些物件現在都沒有人引用了。

這個時候,如果新生代預先分配的記憶體空間被佔滿了,那麼我們的**此時要新建立乙個物件的時候,發現新生代空間滿了,怎麼辦?

這個時候就會觸發一次新生代的垃圾**,也稱為「minor gc」或"young gc",它會嘗試把新生代中沒有人引用的物件給**掉,釋放空間。

下圖表達了這一過程:

長期存活的物件什麼時候進入老年代

接下來我們談論乙個話題,靜態變數引用的長期存活的物件是什麼時候進入老年代的。

上文我們了解到,新生代的物件會經歷一次次的垃圾**,而被靜態變數引用的物件因為一直被引用,所以一直不會被**,所以此時jvm就有了一條規定。

如果新生代中的物件,在經歷了15次垃圾**後,依然堅挺的存活著,那就證明它是個"老年人ykgarb"了,然後它會被轉移到老年代中。

老年代就是存放這些年齡比較大的物件的。

那麼老年代中的物件會被垃圾**嗎?

答案是肯定的,因為老年代裡的物件隨著**的執行,也是可以不再被任何人引用的,就需要垃圾**了。

或者說,隨著越來越多的物件進入老年代,老年代的記憶體也會被佔滿,所以一定是要對老年代進行垃圾**的。

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

總結今天就給大家準備了這麼多內容,可能有些小夥伴覺得還沒看夠,這些內容都比較簡單,我已經會了,有沒有更深入的東西呢?

別急,學習是循序漸進的事情,王子是想要用最簡單的大白話來和小夥伴們一起討論jvm的原理的,同時也想找一些案例來和大家一起**,印象會更深刻。

所以今天小夥伴們了解到這裡就可以了,讓我們在後續的文章中不見不散,深入討論些更深層的www.cppcns.com內容吧。

聊聊JVM分代模型 年輕代 老年代 永久代

jvm記憶體的乙個分代模型 年輕代 老年代 永久代。注 在1.8以後,永久代被移除,轉而用元空間代替。這裡主要是介紹一下概念。1.背景引入 1 package com.test.day11 23 public class testjvm 9 10private static void getstri...

JVM的記憶體模型

參考部落格 記憶體模型 每乙個執行緒有乙個工作記憶體和主存獨立,工作記憶體存放主存中變數的值的拷貝 當資料從主記憶體複製到工作儲存時,必須出現兩個動作 第一,由主記憶體執行的讀 read 操作 第二,由工作記憶體執行的相應的load操作 當資料從工作記憶體拷貝到主記憶體時,也出現兩個操作 第乙個,由...

檢視JVM各個代的記憶體狀態

在linux執行 jmap heap pid 可以看到如下資訊 attaching to process id 23990,please wait.warning the type const char declared in the remote vm in vmstructs localhots...