首頁 > 軟體

Java記憶體區域管理詳解

2022-09-20 22:03:01

1 關於自動記憶體管理

  • Java是由jvm來管理記憶體,包括自動分配以及自動回收,因此它不容易出現記憶體漏失和記憶體溢位問題。
  • C/C++,由程式設計師手動管理記憶體,手動完成:使用前申請記憶體,使用後釋放記憶體。

2 執行時資料區域

Java虛擬機器器在執行Java程式的過程中會把它所管理的記憶體劃分為若干個不同的資料區域。這些區域有各自的用途,以及建立和銷燬的時間。

Java虛擬機器器所管理的記憶體 包括以下幾個執行時資料區域:

2.1 程式計數器

  • 程式計數器(Program Counter Register):儲存當前執行緒所執行的位元組碼指令的記憶體地址。位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令。【通過PC來尋找下一條要執行的指令】。
  • 程式計數器是執行緒私有的。cpu通過輪流分配時間片來執行執行緒,為了執行緒切換後能恢復到正確的執行位置,顯然每個執行緒都需要有一個獨立的程式計數器
  • 如果執行緒正在執行的是一個Java方法,PC記錄的是正在執行的位元組碼指令的地址;如果正在執行的是本地(Native)方法,這個計數器值則應為空。【todo 為什麼本地方法時為空】

2.2 虛擬機器器棧

  • 虛擬機器器棧描述的是Java方法執行的執行緒記憶體模型:每個方法被執行的時候,jvm會同步建立一個棧幀[後續章節詳解](Stack Frame)用於儲存區域性變數表、運算元棧、動態連線、方法出口等資訊
  • 虛擬機器器棧也是執行緒私有的,它的生命週期與執行緒相同。

2.2.1 區域性變數表

JDK8之前,對jvm記憶體的認知只停留在:堆記憶體(Heap)和棧記憶體(Stack),而“棧”通常就是指這裡講的虛擬機器器棧,或者更多的情況下只是指虛擬機器器棧中區域性變數表部分。

  • 區域性變數表儲存:基本資料型別(八種:boolean、byte、char、short、int、 float、long、double)、物件參照(可能是一個指向物件起始 地址的參照指標)和returnAddress 型別(指向了一條位元組碼指令的地址)。
  • 區域性變數表所需的記憶體空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的區域性變數空間是完全確定 的,在方法執行期間不會改變區域性變數表的大小。【這裡的大小僅值變數槽的數量】
  • 區域性變數表以區域性變數槽(Slot)來儲存資料,其中64位元長度的long和 double型別的資料會佔用兩個變數槽,其餘的資料型別只佔用一個。(通常一個槽佔據N個位元組,N大小由不同的虛擬機器器實現決定)。

關於棧的記憶體資料分析,在後續章節中會有更深入分析,在本節中這裡僅引入概念。

2.2.2 運算元棧

關於方法呼叫時的進棧跟出棧的原理,在《深入理解計算機系統》系列筆記的後續章節中會進行總結。

2.3 本地方法棧

  • 本地方法棧(Native Method Stacks)與虛擬機器器棧作用是非常相似的,其區別只是虛擬機器器棧為虛擬機器器執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機器器使用到的本地(Native) 方法服務
  • 執行緒私有的。

2.4 堆

  • Java堆(Java Heap)儲存物件的範例
  • Java堆是執行緒共用的,且比較大的一塊記憶體區域,在虛擬機器器啟動時建立
  • Java堆既可以被實現成固定大小的,也可以是可延伸的,不過當前主流的Java虛擬機器器都是按照可延伸來實現的(通過引數-Xmx和-Xms設定)。
  • 可彈性伸縮,但不會超過設定的最大容量,如果在Java堆中沒有記憶體完成範例分配,並且堆也無法再 擴充套件時,Java虛擬機器器將會丟擲OutOfMemoryError異常
  • Java堆可以處於物理上不連續的記憶體空間中,但在邏輯上它應該 被視為連續的。

2.5 方法區

  • 方法區儲存已被虛擬機器器載入的class的型別資訊、常數、靜態變數、即時編譯器編譯後的程式碼快取等資料。
  • 方法區是執行緒共用
  • 方法區不需要連續的記憶體、可以選擇固定大小或者可延伸,甚至還可以選擇不實現垃圾收集(因為這部分記憶體通常不滿足回收條件)
  • 方法區無法滿足新的記憶體分配需求時,將丟擲 OutOfMemoryError異常。

2.5.1 執行時常數池

  • 存放編譯期生成的各種字面量與符號參照
  • 執行時常數池(Runtime Constant Pool)是方法區的一部分

3 直接記憶體

  • 首先,直接記憶體(Direct Memory)並不是虛擬機器器執行時資料區的一部分,也不是《Java虛擬機器器規範》中 定義的記憶體區域
  • 它可能導致OutOfMemoryError異常出現,有必要了解。

在JDK 1.4中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區 (Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外記憶體,然後通過一個儲存在Java堆裡面的 DirectByteBuffer物件作為這塊記憶體的參照進行操作。這樣能在一些場景中顯著提高效能,因為避免了 在Java堆和Native堆中來回複製資料。
顯然,本機直接記憶體的分配不會受到Java堆大小的限制,但是,既然是記憶體,則肯定還是會受到 本機總記憶體(包括實體記憶體、SWAP分割區或者分頁檔案)大小以及處理器定址空間的限制,一般服務 器管理員設定虛擬機器器引數時,會根據實際記憶體去設定-Xmx等引數資訊,但經常忽略掉直接記憶體,使得 各個記憶體區域總和大於實體記憶體限制(包括物理的和作業系統級的限制),從而導致動態擴充套件時出現 OutOfMemoryError異常。

4 總結

Java記憶體區域及其資料類別概覽:

到此這篇關於Java記憶體區域管理詳解的文章就介紹到這了,更多相關Java記憶體區域管理內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com