天天看點

Java虛拟機學習筆記(一)——記憶體區域與記憶體溢出

一:Java虛拟機的記憶體區域

Java虛拟機在執行Java程式時,會将管理的記憶體分為若幹區域

  • 方法區
  • 虛拟機棧
  • 本地方法棧
  • 程式計數器

1. 程式計數器

(1)概念

目前線程所執行的位元組碼的行号訓示器

  • 如果線程目前執行的是Java方法,則記錄目前執行的位元組碼指令的位址
  • 如果線程目前執行的是Native方法,則為空

(2)作用

位元組碼解釋器的工作就是通過改變該計數器的值,選取下一條需要執行的位元組碼指令

(3)特點

  • 線程私有:由于Java虛拟機的多線程是通過線程輪流切換并配置設定處理器的執行時間的方式實作的,為了線程被切換後能恢複到正确的執行位置,每個線程都要有一個獨立的程式計數器
  • 唯一沒有規定任何OutOfMemoryError記憶體溢出異常情況的區域

2. Java虛拟機棧

(1)概念

每個Java方法的執行,都會建立一個棧幀,用于存儲局部變量表、操作棧、動态連結、方法出口。方法的調用和執行完成的過程,就是棧幀進棧和出棧的過程

局部變量表

内容

存放編譯期可知的各種基本資料類型、對象引用、returnAddress類型(指向一條位元組碼指令的位址)

記憶體空間

在編譯期完成配置設定,方法的局部變量表是固定的,不會改變

  • long和double類型的資料會占用2個局部變量空間
  • byte,boolean,char,short,int,float占用1個局部變量空間

(2)特點

  • 線程私有
  • 生命周期與線程相同

(3)異常

  • 線程請求的棧深度>虛拟機允許的深度:StackOverflowError棧溢出異常
  • 虛拟機棧可以動态擴充,擴充到無法申請記憶體:OutOfMemoryError記憶體溢出異常

3. 本地方法棧

(1)概念

每個Native方法的執行,都會建立一個棧幀,用于存儲局部變量表、操作棧、動态連結、方法出口。方法的調用和執行完成的過程,就是棧幀進棧和出棧的過程

(2)特點

  • 線程私有
  • 生命周期與線程相同

(3)異常

  • 線程請求的棧深度>虛拟機允許的深度:StackOverflowError棧溢出異常
  • 虛拟機棧可以動态擴充,擴充到無法申請記憶體:OutOfMemoryError記憶體溢出異常

4. Java堆

(1)概念

存放對象執行個體,所有的對象執行個體和數組都要在堆上配置設定

(2)特點

  • 被所有線程共享的一塊記憶體區域,在虛拟機啟動時建立。
  • 是垃圾收集器管理的主要區域,也被稱為GC堆
  • 可以處于實體上不連續的記憶體空間中

(3)異常

堆的記憶體沒有完成執行個體配置設定,并且堆無法擴充:OutOfMemoryError記憶體溢出異常

5. 方法區

(1)概念

用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料

運作時常量池
  • 存儲編譯期産生的常量:Class檔案中的常量池(編譯期生成的各種字面量和符号引用)
  • 存儲翻譯後的直接引用
  • 存儲運作期産生的常量

(2)特點

  • 被所有線程共享的一塊記憶體區域,在虛拟機啟動時建立。
  • 可以處于實體上不連續的記憶體空間中
  • 可以不被垃圾收集(垃圾主要是對常量池的回收和對類型的解除安裝)

(3)異常

方法區的記憶體沒有滿足需求,并且無法擴充:OutOfMemoryError記憶體溢出異常

6. 直接記憶體

直接記憶體不屬于虛拟機運作時資料區,但是由于會被忽略,導緻在動态擴充記憶體時,出現OutOfMemoryError記憶體溢出異常

二:Java虛拟機的對象

1. 對象的記憶體區域

Object obj = new Object();
           
  • Object obj:反映到Java棧的本地變量表中,作為一個reference引用類型資料出現
  • new Object():
    • 反映到Java堆中,形成一塊存儲Object類型所有執行個體資料值的結構化記憶體,存儲在執行個體池中
    • 反映到方法區中,存儲Object類型的父類、接口、方法等的位址

2. 對象的通路

(1)使用句柄

  • Java堆劃分出一塊記憶體作為句柄池,存儲句柄
  • Java棧中本地變量表的reference引用類型存儲的就是對象的句柄位址
  • 句柄中包含了對象的執行個體資料的具體位址資訊(執行個體池)和類型資料的具體位址資訊(方法區)

特點:在對象被移動時隻會改變句柄的執行個體資料指針,reference本身不需要修改

(2)使用直接指針

  • Java堆存儲對象的類型資料+執行個體資料
  • Java棧中本地變量表的reference引用類型直接存儲對象位址
  • 指針指向方法區中的對象類型資料

特點:速度更快,節省了一次指針定位的時間開銷

三:記憶體溢出

1. 堆溢出

  • Xms記憶體大小:設定堆的大小
  • Xmx記憶體大小:設定堆的最大值

解決:

  • 記憶體洩露:檢視洩露對象到GC Roots的引用鍊
  • 記憶體溢出:檢查虛拟機的堆參數能否增加,檢查程式的對象生命周期能否縮小

2. 棧溢出

  • Xoss記憶體大小:設定本地棧大小
  • Xss記憶體大小:設定棧記憶體大小

解決

  • 單線程:棧溢出
  • 多線程:記憶體溢出。減少最大堆和棧的容量

3. 方法區溢出或運作時常量池溢出

  • XX:PermSize=記憶體大小:設定方法區大小
  • XX:MaxPermSize=記憶體大小:設定最大方法區

解決

在經常動态生成大量Class的應用中,需要注意類的回收

4. 本機直接記憶體溢出

  • XX:MaxDirectMemorySize:設定直接記憶體的大小