国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

JVM解密: 解構(gòu)類加載與GC垃圾回收機制

這篇具有很好參考價值的文章主要介紹了JVM解密: 解構(gòu)類加載與GC垃圾回收機制。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

一. JVM內(nèi)存劃分

JVM 其實是一個 Java 進程,該進程會從操作系統(tǒng)中申請一大塊內(nèi)存區(qū)域,提供給 Java 代碼使用,申請的內(nèi)存區(qū)域會進一步做出劃分,給出不同的用途。

其中最核心的是棧,堆,方法區(qū)這幾個區(qū)域:

  1. 堆,用來放置 new 出來的對象,類成員變量。
  2. 棧,維護方法之間的調(diào)用關(guān)系,放置局部變量。
  3. 方法區(qū)(舊)/元數(shù)據(jù)區(qū)(新):放的是類加載之后的類對象(.class文件),靜態(tài)變量,二進制指令(方法)。

細分下來 JVM 的內(nèi)存區(qū)域包括以下幾個:程序計數(shù)器,棧,堆,方法區(qū),圖中的元數(shù)據(jù)區(qū)可以理解為方法區(qū)。
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

??程序計數(shù)器:內(nèi)存最小的一塊區(qū)域,保存了下一條要執(zhí)行的指令(字節(jié)碼)的地址,每個線程都有一份。

??:儲存局部變量與方法之間的調(diào)用信息,每一個線程都有一份,但要注意“棧是線程私有的”這種說法是不準確的,私有的意思是我的你是用不了的,但實際上,一個線程棧上的內(nèi)容,是可以被另一個線程使用到的。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

棧在 JVM 區(qū)域劃分中分為兩種,一種是 Java 虛擬機棧,另外一種是本地方法棧,這兩種棧功能非常類似,當方法被調(diào)用時,都會同步創(chuàng)建棧幀來存儲局部變量表、操作數(shù)棧、動態(tài)連接、方法出口等信息。

只不過虛擬機棧是為虛擬機執(zhí)行 Java 方法(也就是字節(jié)碼)服務,而本地方法棧則是給 JVM 內(nèi)部的本地(Native)方法服務的(JVM 內(nèi)部通過 C++ 代碼實現(xiàn)的方法)。
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

??:儲存對象以及對象的成員變量,一個 JVM 進程只有一個,多個線程共用一個堆,是內(nèi)存中空間最大的區(qū)域,Java 堆是垃圾回收器管理的內(nèi)存區(qū)域,后文介紹 GC 的時候細說。

??方法區(qū): JDK 1.8 開始,叫做元數(shù)據(jù)區(qū),存儲了類對象,常量池,靜態(tài)成員變量,即時編譯器編譯后的代碼緩存等數(shù)據(jù);所謂的“類對象”,就是被static修飾的變量或方法就成了類屬性,.java文件會被編譯成.class文件,.class會被加載到內(nèi)存中,也就被 JVM 構(gòu)造成類對象了,類對象描述了類的信息,如類名,類有哪些成員,每個成員叫什么名字,權(quán)限是什么,方法名等;同樣一個 JVM 進程只有一個元數(shù)據(jù)區(qū),多個線程共用一塊元數(shù)據(jù)區(qū)內(nèi)存。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

要注意 JVM 的線程和操作系統(tǒng)的線程是一對一的關(guān)系,每次在 Java 代碼中創(chuàng)建的線程,必然會在系統(tǒng)中有一個對應的線程。

二. 類加載機制

1. 類加載過程

類加載就是把.java文件使用javac編譯為.class文件,從文件(硬盤)被加載到內(nèi)存中(元數(shù)據(jù)區(qū)),得到類對象的過程。(程序要想運行,就需要把依賴的“指令和數(shù)據(jù)”加載到內(nèi)存中)。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
這個圖片所示的類加載過程來自官方文檔,類加載包括三個步驟:Loading, Linking, Initialization
官方文檔:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

下面就來了解一下這三步是在干什么:

第一步,加載Loading),找到對應的.class文件,打開并讀取文件到內(nèi)存中,同時通過解析文件初步生成一個代表這個類的 java.lang.Class 對象。

第二步,連接Linking),作用是建立多個實體之間的聯(lián)系,該過程有包含三個小過程:

  • 驗證Verification),主要就是驗證讀取到的內(nèi)容是不是和規(guī)范中規(guī)定的格式完全匹配,如果不匹配,那么類加載失敗,并且會拋出異常;一個.class文件的格式如下:JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載通過觀察.class文件結(jié)構(gòu),其實.class文件把.java文件的核心信息都保留了下來,只不過是使用二進制的方式重新進行組織了,.class文件是二進制文件,這里的格式有嚴格說明的,哪幾個字節(jié)表示什么,java官方文檔都有明確規(guī)定。 來自官方文檔:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
  • 準備Preparation),給類對象分配內(nèi)存空間(先在元數(shù)據(jù)區(qū)占個位置),并為類中定義的靜態(tài)變量分配內(nèi)存,此時類變量初始值也就都為 0 值了。
  • 解析Resolution),針對字符串常量初始化,將符號引用轉(zhuǎn)為直接引用;字符串常量,得有一塊內(nèi)存空間,存這個字符的實際內(nèi)容,還得有一個引用來保存這個內(nèi)存空間的起始地址;在類加載之前,字符串常量是在.class文件中的,此時這個引用記錄的并非是字符串常量真正的地址,而是它在文件的偏移量/占位符(符號引用),也就是說,此時常量之間只是知道它們彼此之間的相對位置,不知道自己在內(nèi)存中的實際地址;在類加載之后,才會真正的把這個字符串常量給填充到特定的內(nèi)存地址上中,這個引用才能被真正賦值成指定內(nèi)存地址(直接引用),此時字符串常量之間相對位置還是一樣的;這個場景可以想象你看電影時拿著電影票入場入座。

第三步,初始化(Initialization),這里是真正地對類對象進行初始化,特別是靜態(tài)成員,調(diào)用構(gòu)造方法,進行成員初始化,執(zhí)行代碼塊,靜態(tài)代碼塊,加載父類…

??類加載的時機

類加載并不是 Java 程序(JVM)一運行就把所有類都加載了,而是真正用到哪個類才加載哪個;整體是一個“懶加載”的策略;只有需要用的時候才加載(非必要,不加載),就會觸發(fā)以下的加載:

  1. 構(gòu)造類的實例
  2. 調(diào)用這個類的靜態(tài)方法/使用靜態(tài)屬性
  3. 加載子類就會先加載其父類

一旦加載過后后續(xù)使用就不必加載了。

2. 雙親委派模型

雙親委派模型是類加載中的一個環(huán)節(jié),屬于加載階段,它是描述如何根據(jù)類的全限定名找到.class文件的過程。

在 JVM 里面提供了一組專門的對象,用來進行類的加載,即類加載器,當然既然雙親委派模型是類加載中的一部分,所以其所描述找.class文件的過程也是類加載器來負責的。

但是想要找全.class文件可不容易,畢竟.class文件可能在 jdk 目錄里面,可能在項目的目錄里面,還可能在其他特定的位置,因此 JVM 提供了多個類加載器,每一個類加載器負責在一個片區(qū)里面找。

默認的類加載器主要有三個:

  • BootStrapClassLoader,負責加載 Java 標準庫里面的類,如 String,Random,Scanner 等。
  • ExtensionClassLoader,負責加載 JVM 擴展庫中的類,是規(guī)范之外,由實現(xiàn) JVM 的組織(Sun/Oracle),提供的額外的功能。
  • ApplicationClassLoader,負責加載當前項目目錄中自己寫的類以及第三方庫中的類。

除了默認的幾個類加載器,程序員還可以自定義類加載器,來加載其他目錄的類,此時也不是非要遵守雙親委派模型,如 Tomcat 就自定義了類加載器,用來專門加載webapps目錄中的.class文件就沒有遵守。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

雙親委派模型就描述了類加載過程中的找目錄的環(huán)節(jié),它的過程如下:

如果一個類加載器收到了類加載的請求,首先需要先給定一個類的全限定類名,如:“java.lang.String”。

根據(jù)類的全限定名找的過程中它不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此。

因此所有的加載請求最終都應該傳送到頂層的啟動類加載器中,只有當父加載器反饋自己無法完成這個加載請求(它的搜索范圍中沒有找到所需的類)時,子加載器才會嘗試自己去加載(去自己的片區(qū)搜索)。

舉個例子:我們要去找標準庫里面的String.class文件,它的過程大致如下:

  • 首先ApplicationClassLoader類收到類加載請求,但是它先詢問父類加載器是否加載過,即詢問ExtensionClassLoader類是否加載過。
  • 如果ExtensionClassLoader類沒有加載過,請求就會向上傳遞到ExtensionClassLoader類,然后同理,詢問它的父加載器BootstrapClassLoader是否加載過。
  • 如果BootstrapClassLoader沒有加載過,則加載請求就會到BootstrapClassLoader加載器這里,由于BootstrapClassLoader加載器是最頂層的加載器,它就會去標準庫進行搜索,看是否有String類,我們知道String是在標準庫中的,因此可以找到,請求的加載任務完成,這個過程也就結(jié)束了。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
再比如,這里要加載我自己寫的的Test類,過程如下:

  • 首先ApplicationClassLoader類收到類加載請求,但是它先詢問父類加載器是否加載過,即詢問ExtensionClassLoader類是否加載過。
  • 如果ExtensionClassLoader類沒有加載過,請求就會向上傳遞到ExtensionClassLoader類,然后同理,詢問它的父加載器BootstrapClassLoader是否加載過。
  • 如果BootstrapClassLoader沒有加載過,則加載請求就會到BootstrapClassLoader加載器這里,由于BootstrapClassLoader加載器是最頂層的加載器,它就會去標準庫進行搜索,看是否有Test類,我們知道Test類不在標準庫,所以會回到子加載器里面搜索。
  • 同理,ExtensionClassLoader加載器也沒有Test類,會繼續(xù)向下,到ApplicationClassLoader加載器中尋找,由于ApplicationClassLoader加載器搜索的就是項目目錄,因此可以找到Test類,全過程結(jié)束。

如果在ApplicationClassLoader還沒有找到,就會拋出異常。
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
總的來說,雙親委派模型就是找.class文件的過程,其實也沒啥,就是名字挺哄人。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

之所以有上述的查找順序,大概是因為 JVM 代碼是按照類似于遞歸的方式來實現(xiàn)的,就導致了從下到上,又從上到下過程,這個順序,最主要的目的,就是為了保證 Bootstrap 能夠先加載,Application 能夠后加載,這就可以避免說因為用戶創(chuàng)建了一些奇怪的類,引起不必要的 bug。

三. GC垃圾回收機制

在 C/C++ 中內(nèi)存空間是需要進行手動釋放,如果沒有手動去釋放那么這塊內(nèi)存空間就會持續(xù)存在,一直到進程結(jié)束,并且堆的內(nèi)存生命周期比較長,不像棧隨著方法執(zhí)行結(jié)束自動銷毀釋放,堆默認是不能自動釋放的,這就可能導致內(nèi)存泄露的問題,進一步導致后續(xù)的內(nèi)存申請操作失敗。

而在 Java 中引入了 GC 垃圾回收機制,垃圾指的是我們不再使用的內(nèi)存,垃圾回收就是把我們不用的內(nèi)存自動釋放了。

GC的好處:

  • 非常省心,使程序員寫代碼更簡單一些,不容易出錯。

GC的壞處:

  • 需要消耗額外的系統(tǒng)資源,也有額外的性能開銷。
  • GC 這里還有一個嚴重的 STW(stop the world)問題,如果有時候,內(nèi)存中的垃圾已經(jīng)很多了,這個時候觸發(fā)一次 GC 就會消耗大量系統(tǒng)資源,其他程序可能就無法正常執(zhí)行了;GC 可能會涉及一些鎖操作,就可能導致業(yè)務代碼無法正常執(zhí)行;極端情況下可會卡頓幾十毫秒甚至上百毫秒。

GC 的實際工作過程包含兩部分:

  1. 找到/判定垃圾。
  2. 再進行垃圾的釋放。

1. 找到需要回收的內(nèi)存

1.1 哪些內(nèi)存需要回收?

Java 程序運行時,內(nèi)存分為四個區(qū),分別是程序計數(shù)器,棧,堆,方法區(qū)。
對于程序計數(shù)器,它占據(jù)固定大小的內(nèi)存,它是隨著線程一起銷毀的,不涉及釋放,那么也就用不到 GC;對于??臻g,函數(shù)執(zhí)行完畢,對應的棧幀自動銷毀釋放了,也不需要 GC;對于方法區(qū),主要進行類加載,雖然需要進行“類卸載”,此時需要釋放內(nèi)存,但是這個操作的頻率是非常低的;最后對于堆空間,經(jīng)常需要釋放內(nèi)存,GC 也是主要針對堆進行釋放的。

在堆空間,內(nèi)存的分布有三種,一是正在使用的內(nèi)存,二是不用了但未回收的內(nèi)存,三是未分配的內(nèi)存,那內(nèi)存中的對象,也有三種情況,對象內(nèi)存全部在使用(相當于對象整體全部在使用),對象的內(nèi)存部分在使用(相當于對象的一部分在使用),對象的內(nèi)存不使用(對象也就使用完畢了),對于這三類對象,前兩類不需要回收,只有最后一類是需要回收的。
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
所以,垃圾回收的基本單位是對象,而不是字節(jié),對于如何找到垃圾,常用有引用計數(shù)法與可達性分析法兩種方式,關(guān)鍵思路是,抓住這個對象,看看到底有沒有“引用”指向它,沒有引用了,它就是需要被釋放的垃圾。

1.2 基于引用計數(shù)找垃圾(Java不采取該方案)

所謂基于引用計數(shù)判斷垃圾,就是給每一個對象分配一個計數(shù)器(整數(shù)),來記錄該對象被多少個引用變量所指,每次創(chuàng)建一個引用指向該對,,計數(shù)器就+1,每次該引用被銷毀了計數(shù)器就–1,如果這個計數(shù)器的值為0則表示該對象需要回收,比如有一個Test對象,它被三個引用所指,所以這個 Test 對象所帶計數(shù)器的值就是3。

//偽代碼:
Test t1 = new Test();
Test t2 = t1;
Test t3 = t1;

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

如果上述的偽代碼是在一個方法中,待方法執(zhí)行完畢,方法中的局部引用變量被銷毀,那么Test對象的引用計數(shù)變?yōu)?code>0,此時就會被回收。

由此可見,基于引用計數(shù)的方案非常簡單高效并且可靠,但是它擁有兩個致命缺陷:

  1. 內(nèi)存空間浪費較多(利用率低), 需要給每個對象分配一個計數(shù)器,如果按照4個字節(jié)來算;代碼中的對象非常少時無所謂,但如果對象特別多了,占用的額外空間就會很多,尤其是每個對象都比較小的情況下。
  2. 存在循環(huán)引用的問題,會出現(xiàn)對象既不使用也不釋放的情況,看下面舉例子來分析一下。

有以下一段偽代碼:

class Test {
	Test t = null;
}

//main方法中:
Test t1 = new Test(); // 1號對象, 引用計數(shù)是1
Test t2 = new Test(); // 2號對象, 引用計數(shù)是1
t1.t = t2;            // t1.t指向2號對象, 此時2號對象引用計數(shù)是2
t2.t = t1;            // t1.t指向1號對象, 此時1號對象引用計數(shù)是2

執(zhí)行上述偽代碼,運行時內(nèi)存圖如下:

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

然后,我們把變量t1與t2置為null,偽代碼如下:

//偽代碼:
t1 = null;
t2 = null;

執(zhí)行完上面?zhèn)未a,運行時內(nèi)存圖如下:
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
此時 t1 和 t2 引用銷毀了,一號對象和二號對象的引用計數(shù)都-1,但由于兩個對象的屬性相互指向另一個對象,計數(shù)器結(jié)果都是1而不是0造成對象無法及時得到釋放,而實際上這個兩個對象已經(jīng)獲取不到了(應該銷毀了)。

1.3 基于可達性分析找垃圾(Java采取方案)

Java 中的對象都是通過引用來指向并訪問的,一個引用指向一個對象,對象里的成員又指向別的對象。

所謂可達性分析,就是通過額外的線程,將整個 Java 程序中的對象用鏈式/樹形結(jié)構(gòu)把所有對象串起來,從根節(jié)點出發(fā)去遍歷這個樹結(jié)構(gòu),所有能訪問到的對象,標記成“可達”,不能訪問到的,就是“不可達”,JVM 有一個所有對象的名單(每 new 一個對象,JVM 都會記錄下來,JVM 就會知道一共有哪些對象,每個對象的地址是什么),通過上述遍歷,將可達的標記出來,剩下的不可達的(未標記的)就可以作為垃圾進行回收了。

可達性分析的起點稱為GC Roots(就是一個Java對象),一個代碼中有很多這樣的起點,把每個起點都遍歷一遍就完成了一次掃描。
JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載

對于這個GCRoots,一般很難被回收,它來源可以分為以下幾種:

  • 在虛擬機棧(棧幀中的本地變量表)中引用的對象,例如各個線程被調(diào)用的方法堆棧中使用到的參數(shù)、局部變量、臨時變量等。
  • 在本地方法棧中 JNI(即通常所說的Native方法)引用的對象。
  • 常量池中引用所指向的對象。
  • 方法區(qū)中靜態(tài)成員所指向的對象。
  • 所有被同步鎖(synchronized 關(guān)鍵字)持有的對象。

可達性分析克服了引用計數(shù)的兩個缺點,但它有自己的問題:

  1. 需要進行類似于 “樹遍歷”的過程,消耗更多的時間,但可達性分析操作并不需要一直執(zhí)行,只需要隔一段時間執(zhí)行一次尋找不可達對象,確定垃圾就可以,所以,慢一下點也是沒關(guān)系的,雖遲,但到。
  2. 可達性分析過程,當前代碼中的對象的引用關(guān)系發(fā)生變化了,還比較麻煩,所以為了準確的完成這個過程,就需要讓其他的業(yè)務暫停工作(STW問題),但 Java 發(fā)展這么多年,垃圾回收機制也在不斷的更新優(yōu)化,STW 這個問題,現(xiàn)在已經(jīng)能夠比較好的應對了,雖不能完全消除,但也已經(jīng)可以讓 STW 的時間盡量短了。

2. 垃圾回收算法

垃圾回收的算法最常見的有以下幾種:

  1. 標記-清除算法
  2. 標記-復制算法
  3. 標記-整理算法
  4. 分代回收算法(本質(zhì)就是綜合上述算法,在堆的不同區(qū)采取不同的策略)

2.1 標記-清除算法

標記其實就是可達性分析的過程,在可達性分析的過程中,會標記可達的對象,其不可達的對象,都會被視為垃圾進行回收。

比如經(jīng)過一輪標記后,標記狀態(tài)和回收后狀態(tài)如圖:

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
我們發(fā)現(xiàn),內(nèi)存是釋放了,但是回收后,未分配的內(nèi)存空間是零散的不是連續(xù)的,我們知道申請內(nèi)存的時候得到的內(nèi)存得是連續(xù)的,雖然內(nèi)存釋放后總的空閑空間很大,但由于未分配的內(nèi)存是碎片化的,就有可能申請內(nèi)存失??;假設(shè)你的主機有 1GB 空閑內(nèi)存,但是這些內(nèi)存是碎片形式存在的,當申請 500MB 內(nèi)存的時候,也可能會申請失敗,畢竟不能保證有一塊大于 500MB 的連續(xù)內(nèi)存空間,這也是標記-清除算法的缺陷(內(nèi)存碎片問題)。

2.2 標記-復制算法

為了解決標記-清除算法所帶來的內(nèi)存碎片化的問題,引入了復制算法。

它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊,每次清理,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的這一塊內(nèi)存空間一次清理掉。

復制算法的第一步還是要通過可達性分析進行標記,得到哪一部分需要進行回收,哪一部分需要保留,不能回收。

標記完成后,會將還在使用的內(nèi)存連續(xù)復制到另外一塊等大的內(nèi)存上,這樣得到的未分配內(nèi)存一直都是連續(xù)的,而不是碎片化的。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
但是,復制算法也有缺陷:

  • 空間利用率低。
  • 如果垃圾少,有效對象多,復制成本就比較大。

2.3 標記-整理算法

標記-整理算法針對復制算法做出進一步改進,其中的標記過程仍然與“標記-清除”算法一致,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向內(nèi)存空間一端移動,然后直接清理掉邊界以外的內(nèi)存。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
回收時是將存活對象按照某一順序(比如從左到右,從上到下的順序)拷貝到非存活對象的內(nèi)存區(qū)域,類似于順序表的刪除操作,會將后面的元素搬運到前面。
解決了標記-復制算法空間利用率低的問題,也沒有內(nèi)存碎片的問題,但是復制的開銷問題并沒有得到解決。

2.4 分代回收

上述的回收算法都有一定的缺陷,分代回收就是將上述三種算法結(jié)合起來分區(qū)使用,分代回收會針對對象進行分類,以熬過的 GC 掃描輪數(shù)作為“年齡”,然后針對不同年齡采取不同的方案。

分代是基于一個經(jīng)驗規(guī)律,如果一個東西存在時間長了,那么接下來大概率也會存在(要沒有早就沒有了)。

我們知道 GC 主要是回收堆上的無用內(nèi)存,我們先來了解一下堆的劃分,堆包括新生代(Young)、老年代(Old),而新生代包括一個伊甸區(qū)(Eden)與兩個幸存區(qū)(Survivor),分代回收算法就會根據(jù)不同的代去采取不同的標記-xx算法。

JVM解密: 解構(gòu)類加載與GC垃圾回收機制,JavaWeb,java,jvm,后端,GC,類加載
在新生代,包括一個伊甸區(qū)與兩個幸存區(qū),伊甸區(qū)存儲的是未經(jīng)受 GC 掃描的對象(年齡為 0),也就是剛剛 new 出來的對象。

幸存區(qū)存儲了經(jīng)過若干輪 GC 掃描的對象,通過實際經(jīng)驗得出,大部分的 Java 對象具有“朝生夕滅”的特點,生命周期非常短,也就是說只有少部分的伊甸區(qū)對象才能熬過第一輪的 GC 掃描到幸存區(qū),所以到幸存區(qū)的對象相比于伊甸區(qū)少的多,正因為大部分新生代的對象熬不過 GC 第一輪掃描,所以伊甸區(qū)與幸存區(qū)的分配比例并不是1:1的關(guān)系,HotSpot 虛擬機默認一個 Eden 和一個 Survivor 的大小比例是 8∶1,正因為新生代的存活率較小,所以新生代使用的垃圾回收算法為標記-復制算法最優(yōu),畢竟存活率越小,對于標記-復制算法,復制的開銷也就很小。

不妨我們將第一個 Survivor 稱為活動空間,第二個 Survivor 稱為空閑空間,一旦發(fā)生 GC,會將 10% 的活動區(qū)間與另外 80% 伊甸區(qū)中存活的對象復制到 10% 的空閑空間,接下來,將之前 90% 的內(nèi)存全部釋放,以此類推。

在后續(xù)幾輪 GC 中,幸存區(qū)對象在兩個 Survivor 中進行標記-復制算法,此處由于幸存區(qū)體積不大,浪費的空間也是可以接受的。

在繼續(xù)持續(xù)若干輪 GC 后(這個對象已經(jīng)再兩個幸存區(qū)中來回考貝很多次了),幸存區(qū)的對象就會被轉(zhuǎn)移到老年代,老年代中都是年齡較老的對象,根據(jù)經(jīng)驗,一個對象越老,繼續(xù)存活的可能性就越大(要掛早掛了),因此老年代的 GC 掃描頻率遠低于新生代,所以老年代采用標記-整理的算法進行內(nèi)存回收,畢竟老年代存活率高,對于標記-整理算法,復制轉(zhuǎn)移的開銷很低。

還要注意一個特殊情況,如果對象非常大,就直接進入老年代,因為大對象進行復制算法,成本比較高,而且大對象也不會很多。文章來源地址http://www.zghlxwxcb.cn/news/detail-686147.html

到了這里,關(guān)于JVM解密: 解構(gòu)類加載與GC垃圾回收機制的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • 【JAVAEE】JVM中垃圾回收機制 GC

    【JAVAEE】JVM中垃圾回收機制 GC

    ? 博主簡介:想進大廠的打工人 博主主頁: @xyk: 所屬專欄:?JavaEE初階 ? 上篇文章我們講了java運行時內(nèi)存的各個區(qū)域。 傳送門:【JavaEE】JVM的組成及類加載過程_xyk:的博客-CSDN博客 對于程序計數(shù)器、虛擬機棧、本地方法棧這三部分區(qū)域而言,其生命周期與相關(guān)線程有關(guān),隨線

    2024年02月16日
    瀏覽(27)
  • JVM基礎(chǔ)知識(內(nèi)存區(qū)域劃分,類加載,GC垃圾回收)

    JVM基礎(chǔ)知識(內(nèi)存區(qū)域劃分,類加載,GC垃圾回收)

    目錄 內(nèi)存區(qū)域劃分 JVM中的棧 JVM中的堆 程序計數(shù)器 方法區(qū)(元數(shù)據(jù)區(qū)) 給一段代碼,某個變量在哪個區(qū)域上? 類加載 類加載時機 雙親委派模型 GC 垃圾回收機制 GC 實際工作過程 1.找到垃圾/判定垃圾 1.可達性分析(Java中的做法) 2.引用計數(shù) 2.清理垃圾 1.標記清除 2.復制算法 3.標記整

    2024年02月07日
    瀏覽(27)
  • 【JVM】JVM執(zhí)行流程 && JVM類加載 && 垃圾回收機制等

    【JVM】JVM執(zhí)行流程 && JVM類加載 && 垃圾回收機制等

    目錄 ??1、JVM是什么? ??2、JVM的執(zhí)行流程(能夠描述數(shù)據(jù)區(qū)5部分) ??3、JVM類加載過程 ??4、雙親委派機制:描述類加載的過程 問題1:類加載器 問題2:什么是雙親委派模型?? 問題3:雙親委派模型的優(yōu)點 ??5、垃圾回收機制(重要,針對的是堆)?? ?問題1:判定對象

    2024年02月15日
    瀏覽(27)
  • 了解 JVM - 認識垃圾回收機制與類加載過程

    了解 JVM - 認識垃圾回收機制與類加載過程

    本篇通過介紹JVM是什么,認識JVM的內(nèi)存區(qū)域的劃分,了解類加載過程,JVM中垃圾回收機制,從中了解到垃圾回收機制中如何找到存活對象的方式,引用計數(shù)與可達性分析的方式,再釋放垃圾對象時使用的方式,標準清除,復制算法,標準整理,分代回收等等,如有錯誤,請在

    2024年02月16日
    瀏覽(34)
  • 【Java】JVM執(zhí)行流程、類加載過程和垃圾回收機制

    【Java】JVM執(zhí)行流程、類加載過程和垃圾回收機制

    JVM,就是Java虛擬機,Java的的程序都是運行在JVM當中。 程序在執(zhí)行之前先要把java源代碼轉(zhuǎn)換成字節(jié)碼(class文件),JVM 首先需要把字節(jié)碼通過一定的方(類加載器(ClassLoader)) 把文件加載到內(nèi)存中的運行時數(shù)據(jù)區(qū)(Runtime Data Area) ,而字節(jié)碼文件是 JVM 的一套指令集規(guī)范,并

    2024年02月16日
    瀏覽(23)
  • 【Java高級應用:深入探索Java編程的強大功能,JVM 類加載機制, JVM 內(nèi)存模型,垃圾回收機制,JVM 字節(jié)碼執(zhí)行,異常處理機制】

    本人詳解 作者:王文峰,參加過 CSDN 2020年度博客之星,《Java王大師王天師》 公眾號:JAVA開發(fā)王大師,專注于天道酬勤的 Java 開發(fā)問題 中國國學、傳統(tǒng)文化和代碼愛好者的程序人生,期待你的關(guān)注和支持!本人外號:神秘小峯 山峯 轉(zhuǎn)載說明:務必注明來源(注明:作者:

    2024年01月16日
    瀏覽(27)
  • 【JVM】垃圾回收 GC

    【JVM】垃圾回收 GC

    垃圾回收(Garbage Collection,GC)是由 Java 虛擬機(JVM)垃圾回收器提供的一種對內(nèi)存回收的一種機制,它一般會在內(nèi)存空閑或者內(nèi)存占用過高的時候?qū)δ切]有任何引用的對象不定時地進行回收。以避免內(nèi)存溢出和崩潰的問題。JVM的垃圾回收算法包括引用類型、引用計數(shù)器法

    2024年01月16日
    瀏覽(19)
  • JVM----GC(垃圾回收)詳解

    JVM----GC(垃圾回收)詳解

    Automatic Garbage Collection (自動垃圾回收)是JVM的一個特性,JVM會啟動相關(guān)的線程,該線程會輪訓檢查heap memeory,并確定哪些是未被引用的(unreferenced),即未被使用的;哪些是被引用的(referenced),即正在使用的。 在C/C++語言中,對象內(nèi)存的分配與回收,是手動進行分配與回收

    2024年02月09日
    瀏覽(20)
  • 02JVM_垃圾回收GC

    02JVM_垃圾回收GC

    在 堆 里面存放著java的所有對象實例,當對象為“死去”,也就是不再使用的對象,就會進行垃圾回收GC 1.1引用計數(shù)器 介紹 在對象中添加一個引用計數(shù)器,當一個對象被其他變量引用時這個對象的引用計數(shù)器加1。當某個變量不再引用這個對象時引用計數(shù)器減1。當這個引用計

    2024年02月09日
    瀏覽(26)
  • 【JVM】JVM垃圾回收GC相關(guān)參數(shù)說明

    -XX:+PrintCommandLineFlags : 輸出JVM啟動參數(shù) -XX:+UseSerialGC :在新生代和老年代使用串行收集器 -XX:SurvivorRatio :設(shè)置eden區(qū)大小和survivior區(qū)大小的比例 -XX:NewRatio :新生代和老年代的比 -XX:+UseParNewGC :在新生代使用并行收集器 -XX:+UseParallelGC :新生代使用并行回收收集器 -XX:+UseParallelO

    2024年02月04日
    瀏覽(52)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包