性能測試之 JVM 概念認識
無論什麼語言,在程式運行過程中,都需要對記憶體進行管理,要知道電腦/伺服器的記憶體不是無限的。例如:C語言中需要對對象的記憶體負責,需要用delete/free來釋放對象;那JAVA中,對象的記憶體管理是由JVM自動管理的。
JVM是很有必要的了解認識的,因為在程式性能調優中極其重要的兩個判斷方向——運行時間和運行空間,都需要具備JVM的知識理解和工具使用,知其所以然才能無往不利
JVM虛機機的歷史和類型
記憶體模型
程式計數器
- 程式計數器是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。在Java虛擬機的概念模型里,位元組碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,它是程式控制流的指示器,分支、循環、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成
- 執行緒私有記憶體
- 唯一一個不會發生任何OutOfMemoryError的區域
Java虛擬機棧
- 每個方法被執行的時候,Java虛 擬機都會同步創建-一個棧幀 ( Stack Frame)用於存儲局部變數表、操作數棧、動態連接、方法出口等資訊。每一個方法被調用直至執行完畢的過程,就對應著一個棧幀在虛擬機棧中從入棧到出棧的過程。
局部變數表存放了編譯期可知的各種Java虛擬機基本數據類型( boolean、byte、 char、 short、 int、float、long、 double) 、對象引用和returnAddress類型 - 執行緒私有記憶體
- 如果執行緒請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;如果Java虛擬機棧容量可以動態擴展,當棧擴展時無法申請到足夠的記憶體會拋出OutOfMemory Error異常
本地方法棧
- 與虛擬機棧所發揮的作用是非常相似的,其區別只是虛擬機棧為虛擬機執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機使用到的本地(Native)方法服務。
- 棧深度溢出或者棧擴展失敗時分別拋出StackOverflowError和OutOfMemoryError異常
Java堆
- 幾乎所有的對象實例以及數組都應在堆上分配。
如上圖的一個區域劃分,是比較主流的經典的設計。不是絕對的劃分標準,是存在一些VM/垃圾回收器與如上標準不同 - 執行緒共享記憶體
- 如果在Java堆中沒有記憶體完成實例分配,並且堆也無法再擴展時,Java虛擬機將會拋出OutOfMemoryError異常
方法區
- 用於存儲已被虛擬機載入的類型資訊、常量、靜態變數、即時編譯器編譯後的程式碼快取等數據。有一個別名叫作「非堆」(Non-Heap),目的是與Java堆區分
- 執行緒共享記憶體
- 如果方法區無法滿足新的記憶體分配需求時,將拋出OutOfMemoryError異常。
運行時產量池
- 運行時常量池是方法區的一部分。Class文 件中除了有類的版本、欄位、方法、介面等描述資訊外,還有一項資訊是常量池表,用於存放編譯期生成的各種字面量與符號引用,這部分內容將在類載入後存放到方法區的運行時常量池中。
- 執行緒共享記憶體
- 既然運行時常量池是方法區的一部分,自然受到方法區記憶體的限制,當常量池無法再申請到記憶體時會拋出OutOfMemoryError異常
直接記憶體
- 在JDK 1.4中新加入了NIO類,引入了一種基於通道(Channel) 與緩衝區( Buffer)的I/O方式,它可以使用Native函數庫直接分配堆外記憶體,然後通過一個存儲在Java堆裡面的DirectByteBuffer對象作為這塊記憶體的引用進行操作。這樣能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆中來回複製數據
- 直接記憶體的分配不會受到Java堆大小的限制,但是受到本機總記憶體的限制。
配置虛擬機參數時,會根據實際記憶體去設置VM的參數資訊,但經常忽略掉直接記憶體,使得各個記憶體區域總和大於物理記憶體限制(包括物理的和作業系統級的限制),從而導致動態擴展時出現OutOfMemoryError異常
掃一掃,關注我