首頁>技術>

看了不少資料與大佬的文章,來談談自己的理解,不同的版本更新迭代也比較快,以JDK1.8為例

JDK1.8記憶體結構JVM 記憶體區域1. 堆(Heap-執行緒共享)

主要儲存: New物件、陣列、包括JDK1.7從方法區移過來的字串常量和靜態變數、還有 執行緒分配緩衝區(TLAB)

當前主流的Java虛擬機器都是按照可擴充套件來實現的(透過引數-Xmx和-Xms設定)、如果在Java堆中沒有記憶體完成例項分配,並Java虛擬機器將會丟擲OutOfMemoryError異常。

執行緒分配緩衝區(TLAB)

JVM是被執行緒共享的,而且不斷頻繁的產生新的物件,那麼在併發情況下,為保證執行緒安全需要加鎖,但是這樣物件的分配效率又會大大折扣,因此TLAB出現,每個執行緒都會分配一塊獨立的TLAB空間(執行緒私有)

其大小由JVM根據執行的情況計算而得,在TLAB上分配物件時不需要加鎖,因此JVM在給執行緒的物件分配記憶體時會盡量的在TLAB上分配,但如果物件過大的話則仍然是直接使用堆空間分配

2. 方法區(Method Area-執行緒共享)

1、介紹

之前Hotspot方法區的實現是永久代(PermGen space),JRokit的實現是元空間(Metaspace),Oracle公司收購JRokit之後將Hotspot與JRokit合二為一

JDK1.7其實已經開始用元空間(Metaspace)替代永久代(PermGen space),只不過還不完全,直到JDK1.8才完全替代了方法區的實現

2、儲存

JDK1.7之前方法區儲存的有:類資訊(屬性、方法、介面、版本等)、靜態變數、字串常量等。JDK1.7將靜態變數和字串常量池移至堆記憶體,進而減少方法區的OutOfMemoryError

可能有人會問為啥未替換之前方法區易OOM,我們可以這麼理解:一個箱子儲存的容量取決於兩點:箱子本身大小、以及箱子將要裝入東西的容量,以上的講解可以理解為裝入的東西減少,接下來咱們講解箱子本身的容器

3、永久代(PermGen space) VS 元空間(Metaspace)

永久代有(-XX:MaxPermSize)上限,即使不設定也有預設大小,易出現OOM,而預設的元空間只受本地記憶體大小的限制(-XX:MaxMetaspaceSize),例如32位系統鐘的4GB限制

4、String.intern()

String.intern()方法還算比較有意思的,它先在字串常量池查詢是否存在此字串,若存在則返回引用地址;如果不存在則在字串常量池中建立一個等值的字串,再返回此字串的引用地址

相關面試題可瀏覽:https://www.toutiao.com/i6903070438730252811

3. 虛擬機器棧(JVM Stack-執行緒私有)

1、介紹

虛擬機器棧是為Java方法服務的,當我們在呼叫Java方法的時候,每個方法在執行的同時都會建立一個棧幀用於儲存基本資訊【區域性變量表、運算元棧、動態連結、方法出口等】、棧幀隨著方法呼叫而建立,隨著方法結束而銷燬

區域性變量表:存放了編譯期可知的各種Java虛擬機器基本資料型別、物件引用和returnAddress型別

操作樹棧:變數之間的運算操作,結構是先進後出

動態連結:一個方法呼叫另外一個方法,或者訪問其成員變數

4. 本地方法棧(Native Method Stacks-執行緒私有)

1、介紹

本地方法棧(Native Method Stacks)與虛擬機器棧所發揮的作用是非常相似的,

其區別只是虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機器使用到的本地(Native)方法服務

簡單的來說就是:native修飾的方法,非java語言編寫的程式碼,如C或者C++

與虛擬機器棧一樣,本地方法棧也會在棧深度溢位或者棧擴充套件失敗時分別拋StackOverflowError和OutOfMemoryError異常

5 .程式計數器(Program Counter Register-執行緒私有)

1、介紹

程式計數器(Program Counter Registe)是當前執行緒所執行的位元組碼的行號指示器, 如果執行 的是java 方法,記錄的是位元組碼指令的地址;如果是 Native 方法,則為Undefined

這個記憶體區域是唯一一個在虛擬機器中沒有規定任何 OutOfMemoryError 情況的區域。

直接記憶體

1、介紹

在JDK 1.4中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可以使用Native函式庫直接分配堆外記憶體,然後透過一個儲存在Java堆裡面的DirectByteBuffer物件作為這塊記憶體的引用進行操作

簡單的來說就是NIO支援Java訪問直接記憶體,透過對映快取的方式,提高效能,避免了重複copy的方式

2、使用場景

直接記憶體 並不是 JVM 執行時資料區的一部分, 但也會被頻繁的使用;

通常,訪問直接記憶體的速度要優於Java堆。即讀寫效能高。因此出於效能考慮,讀寫大且頻繁的場合可能會考慮使用直接記憶體。前提是能帶來很明顯的效能提升

3、引數設定

直接記憶體的大小設定(-XX:MaxDirectMemorySize)

如果不指定,預設與堆的最大值-Xmx引數值一致,因此忽略直接記憶體的引數設定,可能使得各個記憶體區域總和大於物理記憶體限制,從而動態擴充套件時出現OOM。直接記憶體的大小也受限於作業系統給出的最大記憶體

疑問1.程式計數器記錄native方法,數值為(Undefined)是如何定位呢?

native是非java程式碼編寫的,而是由C或C++實現的,因此JVM獲取不到native實現,只能透過系統指令去呼叫native方法,native方法由原生平臺直接執行,並不需要理會抽象的JVM層面記錄的Undefined——原生的CPU上真正的PC程式計數器是怎樣就是怎樣。就像是一個用C或C++寫的多執行緒程式,它線上程切換有它們自己的一套定位記錄方式。

結語:

一個滬漂90後普通程式設計師,只專注分享乾貨

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 華為裝置配置VRRP,實現裝置閘道器冗餘備份