導航:首頁 > 源碼編譯 > jvm源碼初版

jvm源碼初版

發布時間:2022-12-11 20:20:10

① jvm 基礎篇-(4)-對象動態年齡計算規則

還沒達到,大牛程度,可以看源碼,看動態計算對象年齡的程度呦~

-XX:MaxTenuringThreshold=X X默認是15,15的含義是從eden-->survivor 對象年齡+1,survivor-->eden 對象年齡+1,直到年齡達到15後開始進入old Generation。

Hotspot遍歷所有對象時, 按照年齡從小到大對其所佔用的大小進行累積,當累積的某個年齡大小超過了survivor區的一半時,取這個年齡和MaxTenuringThreshold中更小的一個值,作為新的晉升年齡閾值。
eg:
eg:Survivor區 = 64M,desired survivor = 32M,此時Survivor區中age<=2的對象累計大小為41M,41M大於32M,所以晉升年齡閾值被設置為2,下次Minor GC時將年齡超過2的對象被晉升到老年代。就會導致old generation 快速填滿,觸發old gc(old gc 有三處STW),所以這是建議調整 -XX:SurvivorRatio 參數。

1、如果固定按照MaxTenuringThreshold設定的閾值作為晉升條件: a)MaxTenuringThreshold設置的過大,原本應該晉升的對象一直停留在Survivor區,直到Survivor區溢出,一旦溢出發生,Eden+Svuvivor中對象將不再依據年齡全部提升到老年代,這樣對象老化的機制就失效了。 b)MaxTenuringThreshold設置的過小,「過早晉升」即對象不能在新生代充分被回收,大量短期對象被晉升到老年代,老年代空間迅速增長,引起頻繁的Major GC。分代回收失去了意義,嚴重影響GC性能。

2、相同應用在不同時間的表現不同:特殊任務的執行或者流量成分的變化,都會導致對象的生命周期分布發生波動,那麼固定的閾值設定,因為無法動態適應變化,會造成和上面相同的問題。

總結來說,為了更好的適應不同程序的內存情況, 虛擬機並不總是要求對象年齡必須達到Maxtenuringthreshhold再晉級老年代

傳送門 jvm-對象年齡(-XX:+PrintTenuringDistribution)

② JVM-安全點

Total time for which application threads were stop 超級長時間,這行日誌代表什麼,以及為什麼時間會這么長

當GC發生時,每個線程只有進入了SafePoint才算是真正掛起,也就是真正的停頓,這個日誌的含義是整個GC過程中STW的時間,配置了 -XX:+PrintGCApplicationStoppedTime 這個參數才會列印這個信息。

重點: 第一個 2.81 seconds 是JVM啟動後的秒數,第二個 2.6 seconds 是 JVM發起STW的開始到結束的時間。特別地,如果是GC引發的STW,這條內容會緊挨著出現在GC log的下面。

有關安全點的詳細說明,請移步:
JVM源碼分析之安全點safepoint
[java JVM] Hotspot GC研究- GC安全點 (Safepoint&Stop The World)

等待所有用戶線程進入安全點後並阻塞,做一些全局性操作的行為。

配置 -XX:+PrintSafepointStatistics –XX:PrintSafepointStatisticsCount=1 參數,虛擬機會列印如下日誌文件:

RevokeBias、BulkRevokeBias、偏向鎖取消情況。
Deoptimize、
G1IncCollectionPause GC GC 執行情況。

分析 -XX:+PrintSafepointStatistics –XX:PrintSafepointStatisticsCount=1 產生的日誌信息基本上STW的原因都是RevokeBias或者BulkRevokeBias。這個是撤銷偏向鎖操作,雖然每次暫停的 時間很短,但是特別頻繁出現也會很耗時。

一些高並發的系統中,禁掉JVM偏向鎖優化,可以提升系統的吞吐量 。禁用偏向鎖的參數為: -XX:-UseBiasedLocking

R大分析類似情況
調優建議
各種JVM參數說明
stw分析
R大的博客
安全點 stw說明
偏向鎖

③ 簡述jvm工作原理

Java是一種技術,它由四方面組成:Java編程語言、Java類文件格式、Java虛擬機和Java應用程序介面(Java API)。

運行期環境代表著Java平台,開發人員編寫Java代碼(.java文件),然後將之編譯成位元組碼(.class文件),再然後位元組碼被裝入內存,一旦位元組碼進入虛擬機,它就會被解釋器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行。

Java平台由Java虛擬機和Java應用程序介面搭建,Java語言則是進入這個平台的通道,用Java語言編寫並編譯的程序可以運行在這個平台上。

在Java平台的結構中, 可以看出,Java虛擬機(JVM) 處在核心的位置,是程序與底層操作系統和硬體無關的關鍵。它的下方是移植介面,移植介面由兩部分組成:適配器和Java操作系統, 其中依賴於平台的部分稱為適配器;JVM 通過移植介面在具體的平台和操作系統上實現;在JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application) 和小程序(Java applet) 可以在任何Java平台上運行而無需考慮底層平台, 就是因為有Java虛擬機(JVM)實現了程序與操作系統的分離,從而實現了Java 的平台無關性。

JVM在它的生存周期中有一個明確的任務,那就是運行Java程序,因此當Java程序啟動的時候,就產生JVM的一個實例;當程序運行結束的時候,該實例也跟著消失了。下面我們從JVM的體系結構和它的運行過程這兩個方面來對它進行比較深入的研究。

1、Java虛擬機的體系結構

·每個JVM都有兩種機制:

①類裝載子系統:裝載具有適合名稱的類或介面

②執行引擎:負責執行包含在已裝載的類或介面中的指令

·每個JVM都包含:

方法區、Java堆、Java棧、本地方法棧、指令計數器及其他隱含寄存器

2、Java代碼編譯和執行的整個過程

也正如前面所說,Java代碼的編譯和執行的整個過程大概是:開發人員編寫Java代碼(.java文件),然後將之編譯成位元組碼(.class文件),再然後位元組碼被裝入內存,一旦位元組碼進入虛擬機,它就會被解釋器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行。

(1)Java代碼編譯是由Java源碼編譯器來完成,也就是Java代碼到JVM位元組碼(.class文件)的過程。

2)Java位元組碼的執行是由JVM執行引擎來完成

Java代碼編譯和執行的整個過程包含了以下三個重要的機制:

·Java源碼編譯機制

·類載入機制

·類執行機制

 

(1)Java源碼編譯機制

Java 源碼編譯由以下三個過程組成:

①分析和輸入到符號表

②註解處理

③語義分析和生成class文件

最後生成的class文件由以下部分組成:

①結構信息:包括class文件格式版本號及各部分的數量與大小的信息

②元數據:對應於Java源碼中聲明與常量的信息。包含類/繼承的超類/實現的介面的聲明信息、域與方法聲明信息和常量池

③方法信息:對應Java源碼中語句和表達式對應的信息。包含位元組碼、異常處理器表、求值棧與局部變數區大小、求值棧的類型記錄、調試符號信息

(2)類載入機制 JVM的類載入是通過ClassLoader及其子類來完成的

④ JVM原理是什麼

首先這里澄清兩個概念:JVM實例和JVM執行引擎實例,JVM實例對應了一個獨立運行的Java程序,而JVM執行引擎實例則對應了屬於用戶運行程序的線程;也就是JVM實例是進程級別,而執行引擎是線程級別的。JVM是什麼?—JVM的生命周期JVM實例的誕生:當啟動一個Java程序時,一個JVM實例就產生了,任何一個擁有publicstaticvoidmain(String[]args)函數的class都可以作為JVM實例運行的起點,既然如此,那麼JVM如何知道是運行classA的main而不是運行classB的main呢?這就需要顯式的告訴JVM類名,也就是我們平時運行Java程序命令的由來,如JavaclassAhelloworld,這里Java是告訴os運行SunJava2SDK的Java虛擬機,而classA則指出了運行JVM所需要的類名。JVM實例的運行:main()作為該程序初始線程的起點,任何其他線程均由該線程啟動。JVM內部有兩種線程:守護線程和非守護線程,main()屬於非守護線程,守護線程通常由JVM自己使用,Java程序也可以標明自己創建的線程是守護線程。JVM實例的消亡:當程序中的所有非守護線程都終止時,JVM才退出;若安全管理器允許,程序也可以使用Runtime類或者System.exit()來退出。JVM是什麼?—JVM的體系結構粗略分來,JVM的內部體系結構分為三部分,分別是:類裝載器(ClassLoader)子系統,運行時數據區,和執行引擎。下面將先介紹類裝載器,然後是執行引擎,最後是運行時數據區1、類裝載器,顧名思義,就是用來裝載.class文件的。JVM的兩種類裝載器包括:啟動類裝載器和用戶自定義類裝載器,啟動類裝載器是JVM實現的一部分,用戶自定義類裝載器則是Java程序的一部分,必須是ClassLoader類的子類。(下面所述情況是針對SunJDK1.2)動類裝載器:只在系統類(JavaAPI的類文件)的安裝路徑查找要裝入的類用戶自定義類裝載器:系統類裝載器:在JVM啟動時創建,用來在CLASSPATH目錄下查找要裝入的類其他用戶自定義類裝載器:這里有必要先說一下ClassLoader類的幾個方法,了解它們對於了解自定義類裝載器如何裝載.class文件至關重要。(Stringname,bytedata[],intoffset,intlength) (Stringname,bytedata[],intoffset,intlength,);(Stringname) (Classc) defineClass用來將二進制class文件(新類型)導入到方法區,也就是這里指的類是用戶自定義的類(也就是負責裝載類)findSystemClass通過類型的全限定名,先通過系統類裝載器或者啟動類裝載器來裝載,並返回Class對象。ResolveClass:讓類裝載器進行連接動作(包括驗證,分配內存初始化,將類型中的符號引用解析為直接引用),這里涉及到Java命名空間的問題,JVM保證被一個類裝載器裝載的類所引用的所有類都被這個類裝載器裝載,同一個類裝載器裝載的類之間可以相互訪問,但是不同類裝載器裝載的類看不見對方,從而實現了有效的屏蔽。2、執行引擎:它或者在執行位元組碼,或者執行本地方法要說執行引擎,就不得不的指令集,每一條指令包含一個單位元組的操作碼,後面跟0個或者多個操作數。(一)指令集以棧為設計中心,而非以寄存器為中心這種指令集設計如何滿足Java體系的要求:平台無關性:以棧為中心使得在只有很少register的機器上實現Java更便利compiler一般採用stack向連接優化器傳遞編譯的中間結果,若指令集以stack為基礎,則有利於運行時進行的優化工作與執行即時編譯或者自適應優化的執行引擎結合,通俗的說就是使編譯和運行用的數據結構統一,更有利於優化的開展。網路移動性:class文件的緊湊性。安全性:指令集中絕大部分操作碼都指明了操作的類型。(在裝載的時候使用數據流分析期進行一次性驗證,而非在執行每條指令的時候進行驗證,有利於提高執行速度)。(二)執行技術主要的執行技術有:解釋,即時編譯,自適應優化、晶元級直接執行其中解釋屬於第一代JVM,即時編譯JIT屬於第二代JVM,自適應優化(目前Sun的HotspotJVM採用這種技術)則吸取第一代JVM和第二代JVM的經驗,採用兩者結合的方式自適應優化:開始對所有的代碼都採取解釋執行的方式,並監視代碼執行情況,然後對那些經常調用的方法啟動一個後台線程,將其編譯為本地代碼,並進行仔細優化。若方法不再頻繁使用,則取消編譯過的代碼,仍對其進行解釋執行。3、運行時數據區:主要包括:方法區,堆,Java棧,PC寄存器,本地方法棧(1)方法區和堆由所有線程共享堆:存放所有程序在運行時創建的對象方法區:當JVM的類裝載器載入.class文件,並進行解析,把解析的類型信息放入方法區。(2)Java棧和PC寄存器由線程獨享,在新線程創建時間里(3)本地方法棧:存儲本地方法調用的狀態上邊總體介紹了運行時數據區的主要內容,下邊進行詳細介紹,要介紹數據區,就不得不說明JVM中的數據類型。JVM中的數據類型:JVM中基本的數據單元是word,而word的長度由JVM具體的實現者來決定數據類型包括基本類型和引用類型,(1)基本類型包括:數值類型(包括除boolean外的所有的Java基本數據類型),boolean(在JVM中使用int來表示,0表示false,其他int值均表示true)和returnAddress(JVM的內部類型,用來實現finally子句)。(2)引用類型包括:數組類型,類類型,介面類型前邊講述了JVM中數據的表示,下面讓我們輸入到JVM的數據區首先來看方法區:上邊已經提到,方法區主要用來存儲JVM從class文件中提取的類型信息,那麼類型信息是如何存儲的呢?眾所周知,Java使用的是大端序(big?endian:即低位元組的數據存儲在高位內存上,如對於1234,12是高位數據,34為低位數據,則Java中的存儲格式應該為12存在內存的低地址,34存在內存的高地址,x86中的存儲格式與之相反)來存儲數據,這實際上是在class文件中數據的存儲格式,但是當數據倒入到方法區中時,JVM可以以任何方式來存儲它。類型信息:包括class的全限定名,class的直接父類,類類型還是介面類型,類的修飾符(public,等),所有直接父介面的列表,Class對象提供了訪問這些信息的窗口(可通過Class.forName(「」)或instance.getClass()獲得),下面是Class的方法,相信大家看了會恍然大悟,(原來如此J)getName(),getSuperClass(),isInterface(),getInterfaces(),getClassLoader();static變數作為類型信息的一部分保存指向ClassLoader類的引用:在動態連接時裝載該類中引用的其他類指向Class類的引用:必然的,上邊已述該類型的常量池:包括直接常量(String,integer和floatpoint常量)以及對其他類型、欄位和方法的符號引用(注意:這里的常量池並不是普通意義上的存儲常量的地方,這些符號引用可能是我們在編程中所接觸到的變數),由於這些符號引用,使得常量池成為Java程序動態連接中至關重要的部分欄位信息:普通意義上的類型中聲明的欄位方法信息:類型中各個方法的信息編譯期常量:指用final聲明或者用編譯時已知的值初始化的類變數class將所有的常量復制至其常量池或者其位元組碼流中。方法表:一個數組,包括所有它的實例可能調用的實例方法的直接引用(包括從父類中繼承來的)除此之外,若某個類不是抽象和本地的,還要保存方法的位元組碼,操作數棧和該方法的棧幀,異常表。舉例:classLava{ privateintspeed=5; voidflow(){} classVolcano{ publicstaticvoidmain(String[]args){ Lavalava=newLava(); lava.flow(); } } 運行命令JavaVolcano;(1)JVM找到Volcano.class倒入,並提取相應的類型信息到方法區。通過執行方法區中的位元組碼,JVM執行main()方法,(執行時會一直保存指向Vocano類的常量池的指針)(2)Main()中第一條指令告訴JVM需為列在常量池第一項的類分配內存(此處再次說明了常量池並非只存儲常量信息),然後JVM找到常量池的第一項,發現是對Lava類的符號引用,則檢查方法區,看Lava類是否裝載,結果是還未裝載,則查找「Lava.class」,將類型信息寫入方法區,並將方法區Lava類信息的指針來替換Volcano原常量池中的符號引用,即用直接引用來替換符號引用。(3)JVM看到new關鍵字,准備為Lava分配內存,根據Volcano的常量池的第一項找到Lava在方法區的位置,並分析需要多少對空間,確定後,在堆上分配空間,並將speed變數初始為0,並將lava對象的引用壓到棧中(4)調用lava的flow()方法好了,大致了解了方法區的內容後,讓我們來看看堆Java對象的堆實現:Java對象主要由實例變數(包括自己所屬的類和其父類聲明的)以及指向方法區中類數據的指針,指向方法表的指針,對象鎖(非必需),等待集合(非必需),GC相關的數據(非必需)(主要視GC演算法而定,如對於標記並清除演算法,需要標記對象是否被引用,以及是否已調用finalize()方法)。那麼為什麼Java對象中要有指向類數據的指針呢?我們從幾個方面來考慮首先:當程序中將一個對象引用轉為另一個類型時,如何檢查轉換是否允許?需用到類數據其次:動態綁定時,並不是需要引用類型,而是需要運行時類型,這里的迷惑是:為什麼類數據中保存的是實際類型,而非引用類型?這個問題先留下來,我想在後續的讀書筆記中應該能明白指向方法表的指針:這里和C++的VTBL是類似的,有利於提高方法調用的效率對象鎖:用來實現多個線程對共享數據的互斥訪問等待集合:用來讓多個線程為完成共同目標而協調功過。(注意Object類中的wait(),notify(),notifyAll()方法)。Java數組的堆實現:數組也擁有一個和他們的類相關聯的Class實例,具有相同dimension和type的數組是同一個類的實例。數組類名的表示:如[[LJava/lang/Object表示Object[][],[I表示int[],[[[B表示byte[][][]至此,堆已大致介紹完畢,下面來介紹程序計數器和Java棧程序計數器:為每個線程獨有,在線程啟動時創建,若thread執行Java方法,則PC保存下一條執行指令的地址。若thread執行native方法,則Pc的值為undefinedJava棧:Java棧以幀為單位保存線程的運行狀態,Java棧只有兩種操作,幀的壓棧和出棧。每個幀代表一個方法,Java方法有兩種返回方式,return和拋出異常,兩種方式都會導致該方法對應的幀出棧和釋放內存。幀的組成:局部變數區(包括方法參數和局部變數,對於instance方法,還要首先保存this類型,其中方法參數按照聲明順序嚴格放置,局部變數可以任意放置),操作數棧,幀數據區(用來幫助支持常量池的解析,正常方法返回和異常處理)。本地方法棧:依賴於本地方法的實現,如某個JVM實現的本地方法借口使用C連接模型,則本地方法棧就是C棧,可以說某線程在調用本地方法時,就進入了一個不受JVM限制的領域,也就是JVM可以利用本地方法來動態擴展本身。相信大家都明白JVM是什麼了吧。原文鏈接: http://www.cnblogs.com/chenzhao/archive/2011/08/14/2137713.html

⑤ JVM是什麼

JVM是Java虛擬機,所有的Java程序都在Java虛擬機中運。
JDK是Java開發工具包,用來開發Java程序。

jdk中有一個編譯器,可以把你的java源代碼編譯成可以在虛擬機(jvm)
上運行的位元組碼(中間代碼).

⑥ 求《JVMG1源碼分析和調優豆瓣》全文免費下載百度網盤資源,謝謝~

《JVM G1源碼分析和調優豆瓣》網路網盤pdf最新全集下載:
鏈接: https://pan..com/s/1i8sXLpI7Ey-07u7mGCq2WA

?pwd=c29u 提取碼: c29u
簡介:G1作為JVM中成熟的垃圾回收器,已經廣泛應用在眾多公司的生產環境中。本書詳細介紹G1涉及的基本概念和運行原理,以及調優方法。

⑦ 怎樣查看java中for循環jvm的源碼

方法:使用break。
public class RecTest {
/**
* @param args
*/
public static void main(String[] args) {
for(int i=0; i< 10; i++){
if(i==5){
break; //
}
System.out.print(i+" ");
}
}
}
解釋:本程序實現的是列印:0、1、2、3、4.當i到5的時候滿足條件 i==5;此時執行break操作,跳出本次for循環。

⑧ JVM_位元組碼文件(ClassFile)詳解

我們知道 javac 命令可以將 .java 文件編譯成 .class 文件,而這個 Class 文件 中包含了 Java虛擬機 指令集、符號表以及若干其他輔助信息;最終將在 Java虛擬機 運行。

本文是以 JVM8 為例的。

每一個 Class文件 都有如下的 ClassFile 文件結構:

先簡單介紹一下 ClassFile 文件結構各部分含義:

描述符是表示欄位或方法類型的字元串。

欄位描述符表示類、實例或局部變數的類型。

從上面文法可以看出,欄位描述符中一共有三個類型:

方法描述符包含 0 個或者多個參數描述符以及一個返回值描述符。

看了描述符,可能大家有點疑惑,泛型信息怎麼表示啊?

常量池的通用格式如下:

目前 JVM8 中一共用 14 種常量類型,分別如下:

我們知道要使用一個欄位或者調用一個方法,就必須知道欄位或者方法所屬類符號引用,和欄位的名字和類型,方法的名字和方法參數類型以及方法返回值類型。
但是我們知道類是能繼承的,那麼子類調用父類的方法或者欄位,這里的所屬類符號引用,到底是子類本身還是父類的呢?

我們知道類,方法,欄位都有不同的訪問標志,在 Class 文件 中使用一個 u2 類型數據項來存儲,也就是最多可以有 16 個不同標志位。
在類,方法,欄位中有相同的標志,也有不同的標志,總體規劃,我們可以藉助 Modifier 類的源碼來了解:

在 Modifier 類中,類的訪問標志:

我們知道在 java 中類可以用的修飾符有: public , protected , private , abstract , static , final , strictfp 。

但是我們再看 Class 文件 中類的訪問標志:

仔細看,你會發現有些不同點:

在 Modifier 類中,欄位的訪問標志:

我們知道在 java 中欄位可以用的修飾符有: public , protected , private , static , final , transient 和 volatile 。

但是我們再看 Class 文件 中欄位的訪問標志:

Class 文件 中欄位的訪問標志和 java 中欄位的修飾符差不多,只是多了 ACC_SYNTHETIC 和 ACC_ENUM 兩個標志。

在 Modifier 類中,方法的訪問標志:

我們知道在 java 中方法可以用的修飾符有:
public , protected , private , abstract , static , final , synchronized , synchronized 和 strictfp 。

但是我們再看 Class 文件 中方法的訪問標志:

欄位詳情 field_info 的格式如下:

方法詳情 method_info 的格式如下:

關於 Class 文件 中屬性相關信息,我們再後面章節介紹。

我們可以通過 javap 的命令來閱讀 Class 文件 中相關信息。

這個是最簡單的一個類,沒有任何欄位和方法,只繼承 Object 類,我們來看看它編譯後的位元組碼信息,通過 javap -p -v T.class 的命令:

我們重點關注常量池相關信息,會發現雖然 T.class 很乾凈,但是也有 15 個常量,來我們依次分析:

與之前的例子相比較,多了一個欄位和方法,那麼得到的位元組碼信息如下:

但是你會發現常量池中怎麼沒有這個欄位 name 的 CONSTANT_Fieldref_info 類型的常量呢?
那是因為我們沒有使用這個欄位。

多寫了一個方法 test1 來調用 name 欄位和 test 方法,那麼得到的位元組碼信息如下:

這里定義一個父類 TParent ,有一個公共欄位 name 和方法 say 。子類

⑨ G1從入門到放棄(一)

最近在看關於G1垃圾收集的文章,看了很多國內與國外的資料,本文對G1的這些資料進行了整理。這篇合適JVM垃圾回收有一定基礎的同學,作為G1入門可以看一下,如果要死磕G1實現的內容細節。大家可以找 R大 。 個人認為R大是目前國內JVM領域研究的先驅了,當然R大也是不建議大家去看JVM的源碼的。 為啥別讀HotSpot VM的源碼
G1系列第一篇文章會介紹G1的理論知識,不會做JVM源碼的深入分析。第二篇准備介紹G1實踐中的日誌分析。

G1(Garbadge First Collector)作為一款JVM最新的垃圾收集器,可以解決CMS中Concurrent Mode Failed問題,盡量縮短處理超大堆的停頓,在G1進行垃圾回收的時候完成內存壓縮,降低內存碎片的生成。G1在堆內存比較大的時候表現出比較高吞吐量和短暫的停頓時間,而且已成為Java 9的默認收集器。未來替代CMS只是時間的問題。

G1的內存結構和傳統的內存空間劃分有比較的不同。G1將內存劃分成了多個大小相等的Region(默認是512K),Region邏輯上連續,物理內存地址不連續。同時每個Region被標記成E、S、O、H,分別表示Eden、Survivor、Old、Humongous。其中E、S屬於年輕代,O與H屬於老年代。
示意圖如下:

H表示Humongous。從字面上就可以理解表示大的對象(下面簡稱H對象)。 當分配的對象大於等於Region大小的一半 的時候就會被認為是巨型對象。H對象默認分配在老年代,可以防止GC的時候大對象的內存拷貝。通過如果發現堆內存容不下H對象的時候,會觸發一次GC操作。

在進行Young GC的時候,Young區的對象可能還存在Old區的引用, 這就是跨代引用的問題。為了解決Young GC的時候,掃描整個老年代,G1引入了 Card Table 和 Remember Set 的概念,基本思想就是用空間換時間。這兩個數據結構是專門用來處理Old區到Young區的引用。Young區到Old區的引用則不需要單獨處理,因為Young區中的對象本身變化比較大,沒必要浪費空間去記錄下來。

下圖展示的是 RSet 與 Card 的關系。每個 Region 被分成了多個 Card ,其中綠色部分的 Card 表示該 Card 中有對象引用了其他 Card 中的對象,這種引用關系用藍色實線表示。 RSet 其實是一個HashTable,Key是Region的起始地址,Value是 Card Table (位元組數組),位元組數組下標表示 Card 的空間地址,當該地址空間被引用的時候會被標記為 dirty_card 。

關於RSet結構的維護,可以參考這篇 文章 ,這里不做過多的深入。

SATB的全稱(Snapshot At The Beginning)字面意思是開始GC前存活對象的一個快照。SATB的作用是保證在並發標記階段的正確性。如何理解這句話?
首先要介紹三色標記演算法。

在GC掃描C之前的顏色如下:

在並發標記階段,應用線程改變了這種引用關系

得到如下結果。

在重新標記階段掃描結果如下

這種情況下C會被當做垃圾進行回收。Snapshot的存活對象原來是A、B、C,現在變成A、B了,Snapshot的完整遭到破壞了,顯然這個做法是不合理。
G1採用的是 pre-write barrier 解決這個問題。簡單說就是在並發標記階段,當引用關系發生變化的時候,通過 pre-write barrier 函數會把這種這種變化記錄並保存在一個隊列里,在JVM源碼中這個隊列叫 satb_mark_queue 。在remark階段會掃描這個隊列,通過這種方式,舊的引用所指向的對象就會被標記上,其子孫也會被遞歸標記上,這樣就不會漏標記任何對象,snapshot的完整性也就得到了保證。

這里引用R大對SATB的解釋:

SATB的方式記錄活對象,也就是那一時刻對象snapshot, 但是在之後這裡面的對象可能會變成垃圾, 叫做浮動垃圾(floating garbage),這種對象只能等到下一次收集回收掉。在GC過程中新分配的對象都當做是活的,其他不可達的對象就是死的。
如何知道哪些對象是GC開始之後新分配的呢?
在Region中通過top-at-mark-start(TAMS)指針,分別為prevTAMS和nextTAMS來記錄新配的對象。示意圖如下:

每個region記錄著兩個top-at-mark-start(TAMS)指針,分別為prevTAMS和nextTAMS。在TAMS以上的對象就是新分配的,因而被視為隱式marked。 這里引用R大的解釋。

其中top是該region的當前分配指針,[bottom, top)是當前該region已用(used)的部分,[top, end)是尚未使用的可分配空間(unused)。
(1): [bottom, prevTAMS): 這部分里的對象存活信息可以通過prevBitmap來得知
(2): [prevTAMS, nextTAMS): 這部分里的對象在第n-1輪concurrent marking是隱式存活的
(3): [nextTAMS, top): 這部分里的對象在第n輪concurrent marking是隱式存活的

Young GC 回收的是所有年輕代的Region。 當E區不能再分配新的對象時就會觸發 。E區的對象會移動到S區,當S區空間不夠的時候,E區的對象會直接晉升到O區,同時S區的數據移動到新的S區,如果S區的部分對象到達一定年齡,會晉升到O區。
Yung GC過程示意圖如下:

Mixed GC 翻譯過來叫混合回收。之所以叫混合是因為回收所有的年輕代的Region+部分老年代的Region。
1、為什麼是老年代的 部分 Region?
2、什麼時候觸發Mixed GC?
這兩個問題其實可以一並回答。回收 部分 老年代是參數 -XX:MaxGCPauseMillis ,用來指定一個G1收集過程目標停頓時間,默認值200ms,當然這只是一個期望值。G1的強大之處在於他有一個停頓預測模型(Pause Prediction Model),他會有選擇的挑選 部分 Region,去盡量滿足停頓時間,關於G1的這個模型是如何建立的,這里不做深究。
Mixed GC的觸發也是由一些參數控制。比如 XX: 表示老年代占整個堆大小的百分比,默認值是45%,達到該閾值就會觸發一次Mixed GC。

Mixed GC主要可以分為兩個階段:
1、全局並發標記(global concurrent marking)
全局並發標記又可以進一步細分成下面幾個步驟:

2、拷貝存活對象(Evacuation)
Evacuation階段是全暫停的。它負責把一部分region里的活對象拷貝到空region里去(並行拷貝),然後回收原本的region的空間。Evacuation階段可以自由選擇任意多個region來獨立收集構成收集集合(collection set,簡稱CSet),CSet集合中Region的選定依賴於上文中提到的 停頓預測模型 ,該階段並不evacuate所有有活對象的region,只選擇收益高的少量region來evacuate,這種暫停的開銷就可以(在一定范圍內)可控。

Mixed GC的清理過程示意圖如下:

G1的垃圾回收過程是和應用程序並發執行的,當Mixed GC的速度趕不上應用程序申請內存的速度的時候,Mixed G1就會降級到Full GC,使用的是Serial GC。Full GC會導致長時間的STW,應該要盡量避免。
導致G1 Full GC的原因可能有兩個:

PS: 本文主要參考的國內文章:
java Hotspot G1 GC的一些關鍵技術
Garbage First G1收集器 理解和原理分析
G1: One Garbage Collector To Rule Them All
請教G1演算法的原理
深入理解 Java G1 垃圾收集器
Getting Started with the G1 Garbage Collector !

⑩ 怎樣在ide中進行jvm源碼的調試

按照的方式配置好Mingw32,將其安裝至c:\mingw
將Insight解壓至c:\insight
'make clean',刪除所有的objs,重置編譯環境
'make SYMBOLS=1',編譯mame,別忘了符號編譯選項'SYMBOLS=1'
啟動C:\insight\bin\insight.exe
菜單File->Target Settings->Connection->Target,選擇'Exec'
在下面的ExecArguments裡面添上mame的命令行啟動參數,如ddragon2
File->Open,載入剛剛編譯好的mame.exe
Run->Run,啟動程序,然後便可以設置斷點、單步跟蹤了

閱讀全文

與jvm源碼初版相關的資料

熱點內容
dota2怎麼設置國服伺服器地址 瀏覽:212
單片機高電平驅動 瀏覽:115
ios多選文件夾 瀏覽:909
加強行車調度命令管理 瀏覽:243
伺服器已禁用什麼意思 瀏覽:150
部隊命令回復 瀏覽:755
神奇寶貝伺服器地圖怎麼設置 瀏覽:382
加密演算法輸出固定長度 瀏覽:862
程序員去重慶還是武漢 瀏覽:121
伺服器如何撤銷網頁登錄限制 瀏覽:980
微信公眾平台php開發視頻教程 瀏覽:628
怎麼看蘋果授權綁定的app 瀏覽:255
壓縮機單級壓縮比 瀏覽:380
linux測試php 瀏覽:971
什麼時候梁旁邊需要加密箍筋 瀏覽:40
微信清粉軟體源碼 瀏覽:717
matlabdoc命令 瀏覽:550
如何去ping伺服器 瀏覽:75
ecshop安裝php55 瀏覽:817
javaword庫 瀏覽:958