Java虛擬機(一) - 內存區域與內存溢出異常

運行時數據區

1、 PC寄存器/程序計數器(Program Counter Register)

概念:
程序計數器是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。

由於Java虛擬機的多線程是通過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(多核處理器來說是一個內核)都只會執行一條線程中的指令。

爲了線程切換後能恢復到正確的執行位置,需要程序計數器記錄當前線程運行的字節碼指令地址。

每個線程都有一個獨立的程序計數器。

線程私有內存:各個線程之間互不影響,獨立存儲。
程序計數器就屬於線程私有內存

異常:
不會有OutOfMemoryError的情況

2、Java虛擬機棧(Java Vrtual Machine Stacks)

類型:線程私有

概念:
描述Java方法執行的內存模型。

每個方法在執行時都會創建一個棧幀用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。
每個方法從調用直至執行完成的過程,對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

  • 局部變量表
    存放:基本數據類型、對象引用(reference類型)和returnAddress類型(指向了一條字節碼指令的地址)
    64位長度的數據(long和double)會佔用2個局部變量空間(Slot),其餘類型都佔1個。

異常:
(1)如果線程請求的棧深度大於虛擬機所允許的深度,拋出 StackOverflowError
(2)如果虛擬機可以動態擴展(大部分都可以),如果擴展時無法申請到足夠的內存,拋出 OutOfMemoryError

3、本地方法棧

本地方法棧類似於Java虛擬機棧

有些虛擬機(例如Sun HotSpot虛擬機)直接把Java虛擬機棧和本地方法棧合二爲一

區別:
本地方法棧執行native方法
Java虛擬機棧執行Java方法

4、Java堆(Java Heap)

類型:線程共享的內存區域

概念:
Java虛擬機所管理的內存中最大的一塊。
Java堆的唯一目的就是存放對象實例。

Java堆可以處於物理上不連續的內存空間中,只要邏輯上是連續的即可,就像磁盤空間一樣。
通過-Xmx和-Xms控制堆內存的擴展。

Java堆時垃圾手機和管理的主要區域,有時候也被稱爲 “GC堆”。

異常:
如果在堆中沒有內存完成實例分配,並且堆無法再擴展時,拋出OutOfMemoryError

5、方法區

類型:線程共享的內存區域

概念:
用於存儲每個類的結構信息。

例如:運行時常量池(接下來會講)、字段、方法數據、構造函數和普通方法的字節碼內容。還包括一些在類、實例、接口初始化時用到的特殊方法。

方法區是堆的邏輯組成部分,但區別於Java堆。虛擬機在方法區可以不實現垃圾收集與壓縮。

注意:有的博客上寫 “方法區又稱靜態區”,但本人並沒有在權威的資料裏看到這個翻譯,所以建議大家可以結合這個名詞理解,但不要這麼稱呼。(如果有在權威權威資料裏看到這個翻譯,敬請告知)

異常:
當方法區無法滿足內存分配需求時,拋出,OutOfMemoryError異常。

6、運行時常量池

概念:
方法區的一部分,用於存放編譯器生成的各種字面量和符號引用。
在程序運行期間,也可能有新的常量放入池中。

  • 字面量
int i = 1;把整數1賦值給int型變量i,整數1就是Java字面量,
String s = "abc";中的abc也是字面量。
  • 符號引用
    符號引用以一組符號來描述所引用的目標, 符號可以是任何形式的字面量, 只要使用時能夠無歧義的定位到目標即可. 例如, 在Java中, 一個Java類將會編譯成一個class文件. 在編譯時, Java類並不知道所引用的類的實際地址, 因此只能使用符號引用來代替. 比如org.simple.People類引用了org.simple.Language類, 在編譯時People類並不知道Language類的實際內存地址, 因此只能使用符號org.simple.Language來表示Language類的地址.

  • 直接引用
    直接引用可以是:
    直接指向目標的指針.(個人理解爲: 指向方法區中類對象, 類變量和類方法的指針)
    相對偏移量. (指向實例的變量, 方法的指針)
    一個間接定位到對象的句柄.
    我覺得直接引用說白了, 就是程序運行時可以定位到引用的東西(類, 對象, 變量或者方法等)的地址.

其他

1、直接內存

直接內存不是Java虛擬機運行時數據區的一部分。

在JDK1.4中,新加入的NIO類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外內存,通過存儲在堆中的DirectByteBuffer對象作爲這塊內存的引用進行操作。

異常
如果各個內存區域(Java虛擬機內存區域)總和大於物理內存限制(物理和操作系統級的限制),會導致動態擴展時 拋出,OutOfMemoryError。


[1] 周志明 · 深入理解Java虛擬機 :機械工業出版社
[2] 愛飛翔 周志明 等譯 · Java虛擬機規範(Java SE 8 版)機械工業出版社
[3] 字面量,符號引用,直接引用的概念摘自 https://blog.csdn.net/BraveLoser/article/details/82500474

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章