JAVA JVM運行時數據區詳解

一、前言

這是JVM系列文章的第三篇,這篇文章將對整個JVM運行時數據區和GC垃圾回收詳細的介紹。這部分也算是JVM的核心內容瞭。

二、運行時數據區整體概架構

在這裡插入圖片描述

以下是自己的一句話總結:

分為線程私有和線程共享的兩大類,其中程序計數器、虛擬機棧、本地方法棧是屬於線程私有的,堆內存及方法區內存是線程共享的。程序計數器主要是記錄字節碼指令,CPU上下文切換線程,從一個線程切換到另一個線程,需要知道線程執行到哪一步,所以記錄這個指令就是很有必要的,程序計數器無OOM和GC的發生。虛擬機棧裡面是一個個棧幀,每一個棧幀對應著每一個方法,棧幀又是由局部變量表、操作數棧、方法返回值地址、動態鏈接組成。虛擬機棧可能會發生棧溢出異常,即starkoverflow本地方法棧是存放本地方法相關的東西;堆是一塊很大的空間,整體分為2大塊,新生代和老年代,新生代又分瞭Eden區、S0區、S1區,垃圾回收主要發生在新生代,每一個區對應不同的垃圾回收算法;方法區保存的是一些常量、類的基本信息等,方法區對應的實現在JDK7中是永久代,在JDK8中是元空間。

三、程序計數器

用來儲存指向下一條指令的地址,是線程私有的,生命周期和線程的生命周期一致。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

四、虛擬機棧

虛擬機棧是線程私有的,內部保存一個個棧幀,每一個棧幀對應一個Java方法的調用,生命周期和線程的生命周期保持一致。先來看看棧的特點。

在這裡插入圖片描述

1、棧的特點

棧是運行時的單位,而堆是存儲的單位。棧的特點是先進後出,後進先出。

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

可以通過參數-Xss來設置棧空間大小

2、棧幀的內部結構

在這裡插入圖片描述

3、局部變量表

是一個數字數組,主要用於存儲方法參數和定義在方法內的局部變量,這些數據類型包括各類基本數據類型,對象引用等,所需的容量大小是在編譯期確定下來的,在方法運行期間是不會改變局部變量表大小的。

關於Slot的理解:

在這裡插入圖片描述

靜態變量和局部變量的區別:

在這裡插入圖片描述

總結:

在棧幀中,與性能關系最為密切的就是局部變量表,在方法執行時,虛擬機使用局部變量表完成完成方法的傳遞,局部變量表中的數據也是可達性分析中的GC Root,如果一個對象在局部變量表中還有引用,那麼根絕可達性分析算法,這個變量就不屬於垃圾對象,是不會被GC回收的。

4、操作數棧

操作數棧是棧中棧,也可稱為表達式棧,在方法執行過程中,根據字節碼指令,往棧中寫入數據或提取數據,即入棧和出棧。主要用於保存計算過程的中間結果。操作數棧,可以看成是臨時寄存器,計算過程中變量的臨時保存

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

5、動態鏈接

在這裡插入圖片描述

在這裡插入圖片描述

方法重寫的本質

在這裡插入圖片描述

6、方法返回地址

存放調用該方法的PC寄存器的值

在這裡插入圖片描述

五、本地方法棧

管理本地native本地方法,是線程私有的,所謂的本地方法,其實就是一些非Java語言寫的代碼,這部分代碼甚至可以和操作系統CPU進行打交道。

六、堆

堆是內存管理的核心區域,是線程共享的,屬於JVM級別,也就是一個JVM實例就會有一個堆空間,註意的是雖然堆整體上是線程共享的,但是在內部有一小塊空間是線程私有的緩存區TLAB。

幾乎所有的對象實例都是在堆中,堆是GC垃圾回收的重點區域。堆整體可以分為新生代和老年代,新生代又分為Eden區和S0和S1區。

在這裡插入圖片描述

新生代和老年代的比例是1:2,Eden區和s0,s1區所占空間比例是8:1:1

1、設置堆大小的參數

-Xms:用於表示堆區的起始內存,默認情況下,占物理內存大小的64分之一。

-Xmx用於表示堆區的最大內存,默認情況下,占物理內存的四分之一。

通常起始內存和最大內存兩個參數設置成一樣,目的是為瞭GC清理完堆區內存後不需要重新分隔
計算堆區的大小,從而提高性能。
查看設置的參數:
方式一:jps(查看進程)  
            jstat -gc 進程id
方式二:-xx:+printGCDetails

2、對象分配過程

在這裡插入圖片描述

這裡s0和s1誰是空的誰就是to,年齡計數器閾值是15,YGC是在Eden區滿的時候會觸發,s0和s1滿的時候不會觸發YGC,YGC會將s區以及伊甸園區一起GC

關於垃圾回收,頻繁在新生區收集,很少在養老區收集,幾乎不在永久區/元空間收集。

在這裡插入圖片描述

Visualvm是JVM常用調優工具,在JDK的bin下就可以打開

3、堆中的GC

在這裡插入圖片描述

年輕代(Minor GC)觸發機制

在這裡插入圖片描述

老年代GC(Major GC/Full GC)觸發機制

Full GC 觸發機制

在這裡插入圖片描述

4、內存分配策略

在這裡插入圖片描述

5、什麼是TLAB

在這裡插入圖片描述

在這裡插入圖片描述

TLAB表明堆不一定是共享的。

6、堆是分配對象存儲的唯一選擇嗎?

如果經過逃逸分析,一個對象並沒有逃逸出方法的話,那麼就有可能被優化成棧上分配。

逃逸分析手段:

在這裡插入圖片描述

在這裡插入圖片描述

註意:JDK6U23版本後,HotSpot默認已經開啟逃逸分析。所以我們得出一個結論,開發中能使用局部變量的,就不要使用在方法外定義。JDK7後字符串常量池和靜態變量存儲在堆中

七、方法區

方法區可以看做是一塊獨立於堆的內存空間,是線程共享的,主要存儲類信息、運行時常量池等,也會發生OOM,JDK8前成為永久代,JDK8成為元空間。(元空間和永久代最大的區別是,元空間不再使用JVM內存,而是使用瞭本地內存技術)

1、方法區概述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

2、設置方法區內存大小

在這裡插入圖片描述

在這裡插入圖片描述

3、如何解決OOM問題?

在這裡插入圖片描述

4、方法區存儲什麼

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

5、方法區的演進細節

在這裡插入圖片描述

在這裡插入圖片描述

6、方法區的GC

在這裡插入圖片描述

在這裡插入圖片描述

在這裡插入圖片描述

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: