JVM記憶體模型不再是祕密

王子發表於2020-09-29

 

前言

上篇文章我們一起了解了jvm虛擬機器類的載入機制,而且是以一種純大白話進行的一場閒聊,相信小夥伴們應該印象深刻,感興趣的小夥伴可以重溫一下上一篇文章大白話談JVM的類載入機制

當jvm載入了類後,會把需要使用的物件放入到記憶體當中,那麼jvm的記憶體模型是什麼樣的呢?

今天我們就來探索一下jvm的記憶體模型。由於有小夥伴反映想加些圖更容易理解,王子接下來的文章打算用更多的圖例來講解。

 

方法區

很多小夥伴之前也瞭解過jvm的記憶體模型,知道有方法區這個東西,但可能瞭解的不是很詳細。

其實方法區是在JDK1.8以前的版本里存在的一塊記憶體區域,主要就是存放從class檔案里載入進來的類的,而且常量池也是在這塊區域內的。

但是在JDK1.8之後,這塊區域搖身一變,換了名字,叫做“Metaspace”,翻譯過來就是“後設資料空間”的意思。當然它只是改了個名,實現的功能是沒變的。

 

 

程式計數器

假設我們的程式碼是這樣的:

public class Main {
    public static void main(String[] args) {
        SysUser sysUser = new SysUser();
        sysUser.setAvatar("1");
    }
}

這個是我們的java程式碼,是面向我們開發者的,然後會編譯成class位元組碼檔案,在class位元組碼檔案中存放的是一條條的位元組碼命令,他對應了一條條的機器指令,計算機只有讀到機器指令才知道它要幹什麼。

所以當JVM載入類資訊後,實際上就是使用位元組碼執行引擎去執行我們的程式碼編譯出來的一條條位元組碼指令,如下圖。

 

那麼在執行位元組碼指令的時候,jvm是怎麼知道該執行哪條指令了呢?這時候程式計數器就出現了。

它就是用來記錄當前執行的位元組碼指令位置的。

 

 另外,小夥伴們都知道,JVM是支援多執行緒的,所以如果我們開啟了多執行緒,就會有多個執行緒在執行不同的位元組碼指令,為了他們之間的位元組碼指令不會混在一起,所以每個執行緒都會有自己的程式計數器,用來記錄每個執行緒自己的指令現在執行到哪一條了,如下圖:

 

 

JAVA虛擬機器棧

我們現在知道,jvm執行class中指令時是通過程式計數器來鎖定執行的指令位置的,但是在我們執行的方法裡,會有很多的區域性變數等資料,虛擬機器棧就是用來儲存方法的區域性變數的,而且每個執行緒都會有自己的虛擬機器棧,比如我們之前的程式碼:

public class Main {
    public static void main(String[] args) {
        SysUser sysUser = new SysUser();
        sysUser.setAvatar("1");
    }
}

這個程式碼會啟動一個main執行緒,並把區域性變數sysUser儲存到棧中。

如果執行緒執行了一個方法,就會對這個方法呼叫建立一個棧幀,然後就是所謂的壓棧操作(先進後出),如下:

 

 然後我們程式碼繼續執行,呼叫了setAvatar方法,那麼就會繼續建立棧幀,如下:

 

當setAcatar方法執行完畢,就會對方法的棧幀執行出棧操作。

以上就是JAVA虛擬機器棧這一部分的作用,簡單概括就是:呼叫方法就建立棧幀,壓棧,方法執行完就執行出棧操作

 

JAVA堆記憶體

說完了java虛擬機器棧,那我們再來說一個很重要的記憶體區域java堆記憶體,它是用來存放我們程式碼中建立的各種物件的。

還是以剛才的程式碼為例,當我們執行new SysUser()的時候,就建立了一個SysUser例項物件,而這個物件本身又會有很多的屬性和方法,這樣的例項化物件的資料就是存放在堆記憶體中的。

而這個時候我們在棧中儲存的區域性變數實際上存的就是這個物件的記憶體地址,也可以理解為一個引用地址。如下圖:

 

 

 到這裡JVM的記憶體區域已經和小夥伴們介紹完了,給大家來一張整體的記憶體區域圖,以便理解:

 

 

 

其他記憶體區域

除了前文我們介紹的記憶體區域,jdk的api中(io、nio、socket)相關,其實它們的內部已經不是java程式碼了,而是呼叫了native方法呼叫了本地作業系統的一些方法,可能是c語言編寫的或者是一些底層類庫。

在呼叫native方法的時候,執行緒就會對應本地方法棧,這個是於java虛擬機器棧類似的東東,放的就是native方法的各種區域性變數表。

除此之外還有一個區域,是不屬於JVM的,通過NIO的allocateDirect的api,可以在堆外分配記憶體空間,從而直接操作堆外的記憶體空間資料。

有些場景下,堆外記憶體空間會提升效能,這個問題我們之後再逐步探索,今天就不說這個了。

 

總結

本文到這裡就結束啦,王子採納了一些小夥伴的意見,畫了很多圖有助於小夥伴們更好的理解,希望能夠幫到大家。

那我們下篇文章見。

 

 

往期文章推薦:

大白話談JVM的類載入機制

 

相關文章