效能測試之 JVM 概念認識

老僧觀天下發表於2020-06-06

無論什麼語言,在程式執行過程中,都需要對記憶體進行管理,要知道計算機/伺服器的記憶體不是無限的。例如:C語言中需要對物件的記憶體負責,需要用delete/free來釋放物件;那JAVA中,物件的記憶體管理是由JVM自動管理的。


JVM是很有必要的瞭解認識的,因為在程式效能調優中極其重要的兩個判斷方向——執行時間和執行空間,都需要具備JVM的知識理解和工具使用,知其所以然才能無往不利

JVM虛機機的歷史和型別

記憶體模型

程式計數器

  1. 程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。在Java虛擬機器的概念模型裡,位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,它是程式控制流的指示器,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成
  2. 執行緒私有記憶體
  3. 唯一一個不會發生任何OutOfMemoryError的區域

Java虛擬機器棧

  1. 每個方法被執行的時候,Java虛 擬機都會同步建立-一個棧幀 ( Stack Frame)用於儲存區域性變數表、運算元棧、動態連線、方法出口等資訊。每一個方法被呼叫直至執行完畢的過程,就對應著一個棧幀在虛擬機器棧中從入棧到出棧的過程。
    區域性變數表存放了編譯期可知的各種Java虛擬機器基本資料型別( boolean、byte、 char、 short、 int、float、long、 double) 、物件引用和returnAddress型別
  2. 執行緒私有記憶體
  3. 如果執行緒請求的棧深度大於虛擬機器所允許的深度,將丟擲StackOverflowError異常;如果Java虛擬機器棧容量可以動態擴充套件,當棧擴充套件時無法申請到足夠的記憶體會丟擲OutOfMemory Error異常

本地方法棧

  1. 與虛擬機器棧所發揮的作用是非常相似的,其區別只是虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機器使用到的本地(Native)方法服務。
  2. 棧深度溢位或者棧擴充套件失敗時分別丟擲StackOverflowError和OutOfMemoryError異常

Java堆

  1. 幾乎所有的物件例項以及陣列都應在堆上分配。
    如上圖的一個區域劃分,是比較主流的經典的設計。不是絕對的劃分標準,是存在一些VM/垃圾回收器與如上標準不同
  2. 執行緒共享記憶體
  3. 如果在Java堆中沒有記憶體完成例項分配,並且堆也無法再擴充套件時,Java虛擬機器將會丟擲OutOfMemoryError異常

方法區

  1. 用於儲存已被虛擬機器載入的型別資訊、常量、靜態變數、即時編譯器編譯後的程式碼快取等資料。有一個別名叫作“非堆”(Non-Heap),目的是與Java堆區分
  2. 執行緒共享記憶體
  3. 如果方法區無法滿足新的記憶體分配需求時,將丟擲OutOfMemoryError異常。
執行時產量池
  1. 執行時常量池是方法區的一部分。Class文 件中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池表,用於存放編譯期生成的各種字面量與符號引用,這部分內容將在類載入後存放到方法區的執行時常量池中。
  2. 執行緒共享記憶體
  3. 既然執行時常量池是方法區的一部分,自然受到方法區記憶體的限制,當常量池無法再申請到記憶體時會丟擲OutOfMemoryError異常

直接記憶體

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

掃一掃,關注我

相關文章