技術問答集錦(15)JVM記憶體模型

猿碼道發表於2018-04-24

1 JVM組成結構?

JVM組成結構

JVM = 類載入器(classloader) + 執行引擎(execution engine) + 執行時資料區域(runtime data area)

2 JVM執行時資料區域?

JVM執行時資料區域

3 什麼是程式計數器?

  1. 當前執行緒所執行的位元組碼的行號指示器
  2. 執行緒私有,生命週期與執行緒相同;
  3. 在虛擬機器的概念模型裡,位元組碼直譯器工作時就是 通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,如:分支、迴圈、跳轉、異常處理、執行緒恢復(多執行緒切換)等基礎功能;
  4. 如果執行緒正在執行的是一個Java方法,這個計數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果正在執行的是Natvie方法,這個計數器值則為空(undefined);
  5. 程式計數器中儲存的資料所佔空間的大小不會隨程式的執行而發生改變,所以此區域不會出現OutOfMemoryError的情況

4 什麼是虛擬機器棧?

  1. 描述的是Java方法執行的記憶體模型,每個方法被執行的時候都會同時建立一個 棧幀(Stack Frame) 用於儲存 區域性變數表、操作棧、動態連結、方法出口 等資訊。每一個方法被呼叫直至執行完成的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程
  2. 執行緒私有,生命週期與執行緒相同;
  3. 區域性變數表存放了編譯期可知的各種基本資料型別(boolean、byte、char、short、int、float、long、double)、物件引用(reference型別);它不等同於物件本身,根據不同的虛擬機器實現,它可能是一個指向物件起始地址的引用指標,也可能指向一個代表物件的控制程式碼或者其他與此物件相關的位置)和returnAddress型別(指向了一條位元組碼指令的地址)。區域性變數表所需的記憶體空間在編譯期間完成分配,當進入一個方法時,這個方法需要在棧幀中分配多大的區域性變數空間是完全確定的,在方法執行期間不會改變區域性變數表的大小
  4. 該區域可能丟擲以下異常:
    1. 當執行緒請求的棧深度超過最大值,會丟擲 StackOverflowError 異常;
    2. 棧進行動態擴充套件時如果無法申請到足夠記憶體,會丟擲 OutOfMemoryError 異常;

5 什麼是本地方法棧?

  1. 為虛擬機器使用到的 Native 方法服務;
  2. 執行緒私有,生命週期與執行緒相同;
  3. 虛擬機器規範中對本地方法棧中的方法使用的語言、使用的方式與資料結構並沒有強制規定,因此具體的虛擬機器可以自由實現它。甚至有的虛擬機器(譬如Sun HotSpot 虛擬機器)直接就把本地方法棧和虛擬機器棧合二為一;
  4. 與虛擬機器棧一樣,本地方法棧區域也會丟擲StackOverflowError和OutOfMemoryError異常;

6 什麼是堆?

  1. 在虛擬機器啟動時建立,用來存放物件例項,幾乎所有的物件例項都在這裡分配記憶體;
  2. 被所有執行緒共享;
  3. Java堆是垃圾收集器管理的主要區域,因此很多時候也被稱做“GC堆”。如果從記憶體回收的角度看,由於現在收集器基本都是 採用的分代收集演算法,所以Java堆中還可以細分為:新生代和老年代;新生代又有Eden空間、From Survivor空間、To Survivor空間三部分
  4. Java 堆不需要連續記憶體,並且可以通過動態增加其記憶體,增加失敗會丟擲 OutOfMemoryError 異常;

JVM堆引數示意

7 什麼是方法區?

  1. 用於存放 已被載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼 等資料;
  2. 被所有執行緒共享;
  3. 對這塊區域進行垃圾回收的主要目標是 對常量池的回收和對類的解除安裝,但是一般比較難實現,HotSpot 虛擬機器把它當成永久代(Permanent Generation)來進行垃圾回收
  4. Java 堆一樣不需要連續的記憶體,並且可以動態擴充套件,動態擴充套件失敗一樣會丟擲 OutOfMemoryError 異常;

8 什麼是執行時常量池?

  1. 執行時常量池是方法區的一部分;
  2. Class 檔案中的常量池(編譯器生成的各種字面量和符號引用)會在類載入時被放入這個區域
  3. 除了 在編譯期生成的常量,還允許動態生成,例如 String 類的 intern()。這部分常量也會被放入執行時常量池;

在 JDK1.7之前,HotSpot 使用永久代實現方法區;HotSpot 使用 GC 分代實現方法區帶來了很大便利;

從 JDK1.7 開始 HotSpot 開始移除永久代。其中符號引用(Symbols)被移動到 Native Heap中,字串常量和類引用被移動到 Java Heap中。

在 JDK1.8 中,永久代已完全被元空間(Meatspace)所取代。元空間的本質和永久代類似,都是對JVM規範中方法區的實現。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機器中,而是使用本地記憶體。因此,預設情況下,元空間的大小僅受本地記憶體限制

9 什麼是直接記憶體?

  1. 直接記憶體(Direct Memory)並不是虛擬機器執行時資料區的一部分,也不是Java虛擬機器規範中定義的記憶體區域,但是這部分記憶體也被頻繁地使用,而且也可能導致OutOfMemoryError 異常出現
  2. 在 JDK 1.4 中新加入了 NIO 類,引入了一種基於通道(Channel)與緩衝區(Buffer)的 I/O方式,它可以 使用 Native 函式庫直接分配堆外記憶體,然後通過一個儲存在 Java 堆裡的 DirectByteBuffer 物件作為這塊記憶體的引用進行操作。這樣能在一些場景中顯著提高效能,因為避免了在Java 堆和 Native 堆中來回複製資料

相關文章