1. 程式人生 > >2018年7月11日面試總結二:jvm 記憶體模型

2018年7月11日面試總結二:jvm 記憶體模型

二. jvm 記憶體模型劃分

根據JVM規範,JVM 記憶體共分為虛擬機器棧,堆,方法區,程式計數器,本地方法棧五個部分。
這裡寫圖片描述

程式計數器(執行緒私有):
是當前執行緒鎖執行位元組碼的行號治時期,每條執行緒都有一個獨立的程式計數器,這類記憶體也稱為“執行緒私有”的記憶體。正在執行java方法的話,計數器記錄的是虛擬機器位元組碼指令的地址(當前指令的地址)。如果是Natice方法,則為空。

java 虛擬機器棧
也是執行緒私有的。
每個方法在執行的時候也會建立一個棧幀,儲存了局部變數,運算元,動態連結,方法返回地址。
每個方法從呼叫到執行完畢,對應一個棧幀在虛擬機器棧中的入棧和出棧。
通常所說的棧,一般是指在虛擬機器棧中的區域性變數部分。
區域性變數所需記憶體在編譯期間完成分配,
如果執行緒請求的棧深度大於虛擬機器所允許的深度,則StackOverflowError。
如果虛擬機器棧可以動態擴充套件,擴充套件到無法申請足夠的記憶體,則OutOfMemoryError。
本地方法棧(執行緒私有)


和虛擬機器棧類似,主要為虛擬機器使用到的Native方法服務。也會丟擲StackOverflowError 和OutOfMemoryError。

Java堆(執行緒共享)
被所有執行緒共享的一塊記憶體區域,在虛擬機器啟動的時候建立,用於存放物件例項。
對可以按照可擴充套件來實現(通過-Xmx 和-Xms 來控制)
當隊中沒有記憶體可分配給例項,也無法再擴充套件時,則丟擲OutOfMemoryError異常。
方法區(執行緒共享)
被所有方法執行緒共享的一塊記憶體區域。
用於儲存已經被虛擬機器載入的類資訊,常量,靜態變數等。
這個區域的記憶體回收目標主要針對常量池的回收和堆型別的解除安裝。

3.jvm 1.8 記憶體區域劃分
這裡寫圖片描述

程式計數器
每個執行緒一塊記憶體,指向當前正在執行的位元組碼的行號。如果當前執行緒是native方法,則其值為null。

ps(程式計數器記憶體劃分)
因為處理器在一個確定是時刻只會執行一個執行緒中的指令,執行緒切換後,是通過計數器來記錄執行痕跡的,因而可以看出,程式計數器是每個執行緒私有的。
如果執行的是java方法,那麼記錄的是正在執行的虛擬機器位元組碼指令的地址的地址,如果是native方法,計數器的值為空(undefined)。

Java虛擬機器棧

這裡寫圖片描述

ps: 虛擬機器棧中的名詞解釋

區域性變量表:
存放編譯期可知的各種基本資料型別、物件引用型別和returnAddress型別(指向一條位元組碼指令的地址:函式返回地址)。
long、double佔用兩個區域性變數控制元件Slot。
區域性變量表所需的記憶體空間在編譯期確定,當進入一個方法時,方法在棧幀中所需要分配的區域性變數控制元件是完全確定的,不可動態改變大小。
異常:執行緒請求的棧幀深度大於虛擬機器所允許的深度—StackOverFlowError,如果虛擬機器棧可以動態擴充套件(大部分虛擬機器允許動態擴充套件,也可以設定固定大小的虛擬機器棧),但是無法申請到足夠的記憶體—OutOfMemorError。

運算元棧:
後進先出LIFO,最大深度由編譯期確定。棧幀剛建立使,運算元棧為空,執行方法操作時,運算元棧用於存放JVM從區域性變量表複製的常量或者變數,提供提取,及結果入棧,也用於存放呼叫方法需要的引數及接受方法返回的結果。
運算元棧可以存放一個jvm中定義的任意資料型別的值。
在任意時刻,運算元棧都一個固定的棧深度,基本型別除了long、double佔用兩個深度,其它佔用一個深度
動態連線:
每個棧幀都包含一個指向執行時常量池中該棧幀所屬方法的引用,持有這個引用是為了支援方法呼叫過程中的動態連線。Class檔案的常量池中存在有大量的符號引用,位元組碼中的方法呼叫指令就以常量池中指向方法的符號引用為引數。這些符號引用,一部分會在類載入階段或第一次使用的時候轉化為直接引用(如final、static域等),稱為靜態解析,另一部分將在每一次的執行期間轉化為直接引用,這部分稱為動態連線。

方法返回地址:
當一個方法被執行後,有兩種方式退出該方法:執行引擎遇到了任意一個方法返回的位元組碼指令或遇到了異常,並且該異常沒有在方法體內得到處理。無論採用何種退出方式,在方法退出之後,都需要返回到方法被呼叫的位置,程式才能繼續執行。方法返回時可能需要在棧幀中儲存一些資訊,用來幫助恢復它的上層方法的執行狀態。一般來說,方法正常退出時,呼叫者的PC計數器的值就可以作為返回地址,棧幀中很可能儲存了這個計數器值,而方法異常退出時,返回地址是要通過異常處理器來確定的,棧幀中一般不會儲存這部分資訊。
方法退出的過程實際上等同於把當前棧幀出棧,因此退出時可能執行的操作有:恢復上層方法的區域性變量表和運算元棧,如果有返回值,則把它壓入呼叫者棧幀的運算元棧中,調整PC計數器的值以指向方法呼叫指令後面的一條指令。


堆時JVM記憶體佔用最大,管理最複雜的一個區域。唯一的途徑就是存放物件例項:所有的物件例項以及陣列都在堆上進行分配。jdk1.7以後,字串常量從永久代中剝離出來,存放在堆中。堆具有進一步的記憶體劃分。按照GC分代手機角度劃分
這裡寫圖片描述
老年代:2/3的堆空間
年輕代:1/3的堆空間
eden區:8/10 的年輕代
survivor0: 1/10 的年輕代
survivor1:1/10的年輕代

元資料區域
元資料區域取代了1.7版本及以前的永久代。元資料和永久代本質上都時方法區的實現。方法區皴法虛擬機器載入的型別西,靜態變數,常量資料。
引數設定:-XX:MetaspaceSize=18m
-XX:MaxMetaspaceSize=60m
直接記憶體
java.nio 中使用DirectBuffer相關使用(此處未完待續。。。。。。。。。)