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

2022-09-26 08:57:08 字數 4151 閱讀 5926

jvm記憶體的乙個分代模型:年輕代、老年代、永久代。

注:在1.8以後,永久代被移除,轉而用元空間代替。這裡主要是介紹一下概念。

1. 背景引入

1

package

com.test.day11;23

public

class

testjvm 9}

10private

static

void

getstring()

14 }

這段**,在main()方法裡,會週期新的執行getstring()方法,載入副本資料。

首先一旦執行main()方法,那麼就會把main()方法的棧幀壓入main執行緒的j**a虛擬機器棧 如下圖。

然後每次在while迴圈裡,呼叫getstring()方法,就會把getstring()方法的棧幀壓入自己的 j**a虛擬機器棧。

接著在執行getstring()方法的時候,會在j**a堆記憶體裡會建立乙個string物件例項 而且getstring()方法的棧幀裡會有「string」區域性變數去引用j**a堆記憶體裡的 string物件例項。

2. 大部分物件都是存活週期極短的

現在有乙個問題,在上面**中,那個string物件,實際上屬於短暫存活的這麼乙個物件

大家可以觀察一下,在getstring()方法中建立這個物件,然後執行string物件的load()方法, 然後執行完畢之後,getstring()方法就會結束。

一旦方法結束,那麼getstring()方法的棧幀就會出棧,如下圖。

然後接著上篇文章已經說過,此時一旦沒人引用這個string物件了,就會被jvm的垃圾**執行緒給**掉,釋放記憶體空間,如下圖。

然後在main()方法的while迴圈裡,下一次迴圈再次執行getstring()方法的時候,又會走一遍上面那個過程,把 getstring()方法的棧幀壓入j**a虛擬機器棧,然後構造乙個string例項物件放在j**a堆裡。

一旦執行完string物件的load()方法之後,getstring()方法又會結束,再次出棧,然後垃圾回 收釋放掉j**a堆記憶體裡的string物件。

所以其實這個string物件,在上面的**中,是乙個存活週期極為短暫的物件。

可能每次執行getstring()方法的時候,被建立出來,然後執行他的load()方法,接著可能1毫秒之後,就 被垃圾**掉了。

所以從這段**就可以明顯看出來,大部分在我們**裡建立的物件,其實都是存活週期很短的。這種物件,其實在 我們寫的j**a**中,佔到絕大部分的比例

3. 少數物件是長期存活的

但是我們來看另外一段**,假如說咱們用下面的這種方式來實現同樣的功能:

1

package

com.test.day11;23

public

class

testjvm 11}

12private

static

void

getstring()

15 }

上面那段**的意思,就是給testjvm這個類定義乙個靜態變數,也就是「string」,這個testjvm類是在jvm的方法區里的。

然後讓「string」引用了乙個在j**a堆記憶體裡建立的string例項物件,如下圖。

接著在main()方法中,就會在乙個while迴圈裡,不停的呼叫println()方法,做成乙個週期性執行的模式。

這個時候,我們就要來思考一下,這個string例項物件,他是會一直被testjvm的靜態變數引用的,然後會 一 直駐留在j**a堆記憶體裡,是不會被垃圾**掉的。

因為這個例項物件他需要長期被使用,週期性的被呼叫load()方法,所以他就成為了乙個長時間存在的物件。

那麼類似這種被類的靜態變數長期引用的物件,他需要長期停留在j**a堆記憶體裡,這這種物件就是生存週期很長的對 象,他是輕易不會被垃圾**的,他需要長期存在,不停的去使用他。

4. jvm分代模型:年輕代和老年代

現在大家已經看到,其實根據你寫**方式的不同,採用不同的方式來建立和使用物件,其實物件的生存週期是不同 的。

所以jvm將j**a堆記憶體劃分為了兩個區域,乙個是年輕代,乙個是老年代。

其中年輕代,顧名思義,就是把第一種**示例中的那種,建立和使用完之後立馬就要**的物件放在裡面

**改造如下:

1

package

com.test.day11;23

import

j**a.util.hashmap;

4import

j**a.util.map;56

public

class

testjvm 15}

1617

private

static

void

getstring()

2122

private

static

void

printmap()

25 }

上面那段**稍微複雜了點,解釋一下 testjvm的靜態變數「mp」引用了hashmap物件,這是長期需要駐留在記憶體裡使用的。

這個物件會在年輕代里停留一會兒,但是最終會進入老年代,大家看下圖。

進入main() 方法之後,會先呼叫getstring0方法,業務含義是系統啟動就從磁碟載入一次副本資料,這個方法的棧幀會入棧

然後在這個方法裡面建立了乙個string物件,這個物件他是用完就會**,所以是會放在年輕代里的,由棧幀裡的區域性變數來引用

此時對應著下圖:

然後一旦getstring()方法執行完畢了,方法的棧幀就會出棧,對應的年輕代里的string物件也會被**掉,如下圖:

但是接著會執行一段while迴圈**,他會週期性的呼叫replicaprinter的print()方法,去從遠端載入副本資料。

所以hashmap這個物件因為被testjvm類的靜態變數mp給引用了,所以他會長期存在於老年代裡的,持續被使用。

5、為什麼要分成年輕代和老年代?

因為這跟垃圾**有關,

對於年輕代里的物件,他們的特點是建立之後很快就會被**,所以需要用一種垃圾**算 法 。

對於老年代裡的物件,他們的特點是需要長期存在,所以需要另外一種垃圾**演算法。

所以需要分成兩個區域來放不同的物件

6、什麼是永久代?

很簡單,jvm裡的永久代其實就是之前說的方法區。

上面那個圖里的方法區,其實就是所謂的永久代,你可以認為永久代就是放一些類資訊的。

方法區內會不會進行垃圾**?

在以下幾種情況下,方法區里的類會被**。

首先該類的所有例項物件都已經從j**a堆記憶體裡被**

其次載入這個類的classloader已經被**

最後,對該類的class物件沒有任何引用 滿足上面三個條件就可以**該類了。

關於新生代和老年代

目錄前言 一 年輕代 1.1survivor區解釋 二 老年代 三 full gc 總結 感謝 這裡主要記錄一點對於新生代和老年代的整理了解 也叫新生代,顧名思義,主要是用來存放新生的物件。新生代又細分為eden區 survivorfrom區 survivorto區。如果新生物件在eden區無法分配...

詳解JVM的分代模型

前言 本篇文章我們將針對jvm堆記憶體的分代模型做乙個詳細的解析,和大家一起輕鬆理解jvm的分代模型。相信看過其他文章的小夥伴們可能都知道,jvm的分代模型包括 年輕代 老年代 永久代。那麼它們分別代表著什麼角色呢?我們先來看一段 public class main public static vo...

GC新生代物件晉公升到老年代情況總結

物件優先在eden分配,且新生代物件晉公升到老年代有多種情況,現在做乙個總結 1 eden區滿時,進行minor gc,當eden和乙個survivor區中依然存活的物件無法放入到survivor中,則通過分配擔保機制提前轉移到老年代中。2 若物件體積太大,新生代無法容納這個物件,xx preten...