Java的記憶體區域劃分

Hanstrovsky發表於2020-11-14

記憶體分割槽簡介

老生常談的問題了,雖然網上一搜一大把,也很詳細,但是我還是想寫一寫,通過自己的總結整理,加深一下印象。

我不知道學習Java記憶體分割槽有什麼實際作用,但它就是像常識一樣,一個使用Java語言的人不知道記憶體分割槽總感覺差點意思。

Java程式是執行在JVM虛擬機器上的。Java虛擬機器在執行程式時會把其自動管理的記憶體劃分為以下幾個區域:

方法區、堆、程式計數器、虛擬機器棧、本地方法棧。

其中方法區和堆是所有執行緒共享的,而程式計數器、虛擬機器棧和本地方法棧是每個執行緒獨立享有的。

這個不需要死記硬背,當我們瞭解了這幾個區域不同的功能之後,就知道為什麼有些記憶體區域是執行緒共享,而另外一些是執行緒私有的了。

各區域職責劃分

方法區(Method Area)

方法區屬於執行緒共享的記憶體區域,《Java虛擬機器規範》中將其描述為堆的一個邏輯部分,又稱Non-Heap(非堆,目的是與Java堆區分開來),主要用於儲存已被虛擬機器載入的型別資訊、常量、靜態變數、即時編譯器編譯後的程式碼快取等資料。

拿做菜打個比方,我理解方法區就好比一個食譜,它裡面記錄著做菜需要用的食材,加工方式等等。既然是菜譜,那就是公共的東西,不能修改,沒必要每個執行緒都有一份,大家誰需要誰來檢視就好了。所以它是執行緒共享的。

JVM堆(Java Heap)

Java 堆在虛擬機器啟動時建立,是Java 虛擬機器所管理的記憶體中最大的一塊,主要用於存放物件例項,幾乎所有的物件例項都在這裡分配記憶體,是垃圾收集器管理的主要區域,因此很多時候也被稱做GC 堆(Garbage Collection),如果在堆中沒有記憶體完成例項分配,並且堆也無法再擴充套件時,將會丟擲OutOfMemoryError 異常。

這次有點牽強,但還是可以拿做菜打比方。

先說說物件導向。堆是用來儲存物件的,物件是什麼呢?物件是幫助我們做事情的東西,很多人拿洗衣服來說明物件導向程式設計。如果我們程式導向洗衣服,那麼我們需要揉搓、洗滌、擰水、晾曬,每一步都要親力親為;而當我們物件導向時,只需要將衣服扔進洗衣機這個物件,剩下的事就不用管了,輕鬆省力。所以物件,就是幫助我們做事的工具。

在做菜中,物件可以理解為電烤箱、菜刀,這種工具,那麼在程式中,堆就是用來儲存這些工具的,每個人都可以使用這些工具,不會亂套,所以堆也是執行緒共享的。

程式計數器(Program Counter Register):

程式計數器屬於執行緒私有的資料區域,是一小塊記憶體空間,主要代表當前執行緒所執行的位元組碼行號指示器。位元組碼直譯器工作時,通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成。

用做菜來說,程式計數器記錄的是你做菜做到了哪個環節了,顯然不可能每個廚師都恰好在做相同的事,大家需要自己把控自己的進度,所以程式計數器是執行緒私有的。

虛擬機器棧(Java Virtual Machine Stacks):

屬於執行緒私有的資料區域,與執行緒同時建立,總數與執行緒關聯,代表Java方法執行的記憶體模型。每個方法執行時都會建立一個棧楨來儲存方法的的變數表、運算元棧、動態連結方法、返回值、返回地址等資訊。每個方法從呼叫直結束就對於一個棧楨在虛擬機器棧中的入棧和出棧過程。

這個對應的就是做菜的過程了,比如做一道菜需要三步,準備材料→炒菜→出鍋。拿第一步舉例,首先要準備材料,準備材料這個方法就要入棧了,入棧後發現,需要先把菜洗了,那麼洗菜這個方法也要進棧,然後又發現需要先摘菜葉子。我們假定摘菜葉之前沒有其他操作了,那麼之後從棧頂開始先執行摘菜方法,然後摘菜方法彈出,然後執行洗菜方法,然後洗菜方法彈出,再執行準備材料方法,是一個後進先出的順序,前一個彈出,後一個再執行。既然都已經具體到過程了,自然每個執行緒是要隔離開的,不能相互干擾。

本地方法棧(Native Method Stacks):

本地方法棧跟 Java 虛擬機器棧的功能類似,Java 虛擬機器棧用於管理 Java函式的呼叫,而本地方法棧則用於管理本地方法的呼叫。但本地方法並不是 用 Java 實現的,而是由 C 語言實現的(比如Object.hashcode 方法)。 本地方法棧是和虛擬機器棧非常相似的一個區域,它服務的物件是 native方法。你甚至可以認為虛擬機器棧和本地方法棧是同一個區域。

虛擬機器規範無強制規定,各版本虛擬機器自由實現 ,HotSpot 直接把本地方法棧和虛擬機器棧合二為一 。

本地方法棧屬於執行緒私有的資料區域,這部分主要與虛擬機器用到的 Native 方法相關,一般情況下,我們無需關心此區域。

簡單總結一下

Java記憶體區域劃分為5個,重點關注的是堆、棧、和方法區,程式計數器與本地方法棧一般不大涉及到。

方法區與堆是所有執行緒共享的,其餘三個是每個執行緒私有的。

堆是兵器庫,垃圾回收的主要區域,方法區是指揮官,棧是管幹活的。

如果我的理解和表達有不對的地方,歡迎大佬們斧正。

相關文章