深入理解JVM之記憶體區域與記憶體溢位

wustrive's blog發表於2015-12-27

文章目錄

  1. 1. Java記憶體區域與記憶體溢位異常
    1. 1.1. 執行時資料區域
      1. 1.1.1. 程式計數器
      2. 1.1.2. java虛擬機器棧
      3. 1.1.3. 本地方法棧
      4. 1.1.4. Java堆(Java Heap)
      5. 1.1.5. 方法區
      6. 1.1.6. 執行時常量池
      7. 1.1.7. 直接記憶體
    2. 1.2. HotSpot虛擬機器
      1. 1.2.1. 物件的建立
      2. 1.2.2. 物件的訪問定位
    3. 1.3. OOM異常的解決思路
    4. 1.4. 參考

深入理解jvm之記憶體區域與記憶體溢位

Java記憶體區域與記憶體溢位異常

執行時資料區域

深入理解jvm之記憶體區域與記憶體溢位

程式計數器

  • 當前執行緒所執行的位元組碼的行號指示器
  • 當前執行緒私有
  • 不會出現OutOfMemoryError情況

java虛擬機器棧

  • 執行緒私有,生命週期與執行緒相同
  • java方法執行的記憶體模型,每個方法執行的同時都會建立一個棧幀,儲存區域性變數表(基本型別、物件引用)、運算元棧、動態連結、方法出口等資訊
  • StackOverflowError異常:當執行緒請求的棧深度大於虛擬機器所允許的深度
  • OutOfMemoryError異常:如果棧的擴充套件時無法申請到足夠的記憶體

本地方法棧

與虛擬機器棧相似,主要為虛擬機器使用到的Native方法服務,在HotSpot虛擬機器中直接把本地方法棧與虛擬機器棧二合一

Java堆(Java Heap)

java堆是被所有執行緒共享的一塊記憶體區域,在 虛擬機器啟動時建立。此區域的唯一目的就是儲存物件例項。java堆是垃圾收集器管理的主要區域。java堆還可以細分為:新生代與老年代。在細一點有 Eden空間、Form Survivor空間、To Survivor空間等。

  • 可以通過-Xmx和-Xms控制堆的大小
  • OutOfMemoryError異常:當在堆中沒有記憶體完成例項分配,且堆也無法再擴充套件時。

方法區

  • 執行緒間共享
  • 用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料
  • OutOfMemoryError異常:當方法區無法滿足記憶體的分配需求時

執行時常量池

  • 方法區的一部分
  • 用於存放編譯期生成的各種字面量與符號引用
  • OutOfMemoryError異常:當常量池無法再申請到記憶體時

直接記憶體

  • NIO可以使用Native函式庫直接分配堆外記憶體,堆中的DirectByteBuffer物件作為這塊記憶體的引用進行操作
  • 大小不受Java堆大小的限制,受本機(伺服器)記憶體限制
  • OutOfMemoryError異常:系統記憶體不足時

HotSpot虛擬機器

物件的建立

虛擬機器遇到一條new指令時,首先將去檢查這個物件的引數是否在常量池中定位到一個類的符號引用,並且檢查這個符號引用代表的類是否已被載入、解析和初始化過。如果沒有,必須先執行類的載入過程。
在類載入檢查通過後,虛擬機器將為新生物件分配記憶體。物件所需記憶體大小再類載入完成後便可確定。記憶體分配可以採用“指標碰撞”與“空閒列表”的方式。

物件的訪問定位

java程式需要通過棧上的reference資料來操作堆上的具體物件。訪問方式有使用控制程式碼和直接指標兩種。

  • 控制程式碼訪問 java堆中將會劃分出一塊記憶體來作為控制程式碼池,reference中儲存的就是物件的控制程式碼地址,而控制程式碼中包含了物件例項資料與型別資料各自的具體地址資訊
  • 直接指標訪問 java堆物件的佈局中必須考慮如何放置訪問型別資料的相關資訊,reference中儲存的就是物件地址

OOM異常的解決思路

生成Dump快照檔案:

  • 通過jvm引數—XX:-HeapDumpOnOutOfMemoryError可以讓JVM在出現記憶體溢位是Dump出當前的記憶體轉儲快照
  • 用jmap生產dump檔案,win通過工作管理員檢視tomcat的程式pid,linux用ps命令檢視程式pid,然後用jmap命令

先通過記憶體映像分析工具(如Eclipse的Memory Analyzer)進行分析,常見的情況有:

  • 記憶體洩露,物件已經死了,無法通過垃圾收集器進行自動回收,通過找出洩露的程式碼位置和原因,才好確定解決方案;
  • 記憶體溢位,記憶體中的物件都還必須存活著,這說明Java堆分配空間不足,檢查堆設定大小(-Xmx與-Xms),檢查程式碼是否存在物件生命週期太長、持有狀態時間過長的情況。

OOM異常示例:

package oom;

import java.util.ArrayList;
import java.util.List;

/**
 * VM Args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* @ClassName: HeapOOM 
*
 */
public class HeapOOM {
    static class OOMObject{

    }

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<OOMObject>();
        while(true){
            list.add(new OOMObject());
        }
    }
}

相關文章