JVM學習筆記1:Java虛擬機記憶體模型

来源:https://www.cnblogs.com/Zafkiel/archive/2018/10/10/9769805.html
-Advertisement-
Play Games

JVM學習筆記1:Java虛擬機記憶體模型 學習JVM,Java虛擬機對理解Java程式執行過程和Java程式性能調優具有很大幫助。本系列博客旨在由淺到深學習並理解JVM。參考閱讀:《深入理解Java虛擬機 JVM高級特性和最佳實踐》。這個書寫的非常好,推薦有條件的讀者買一本來閱讀,網上也有電子版的。 ...


JVM學習筆記1:Java虛擬機記憶體模型

學習JVM,Java虛擬機對理解Java程式執行過程和Java程式性能調優具有很大幫助。本系列博客旨在由淺到深學習並理解JVM。參考閱讀:《深入理解Java虛擬機-JVM高級特性和最佳實踐》。這個書寫的非常好,推薦有條件的讀者買一本來閱讀,網上也有電子版的。本系列基於HotSpot虛擬機。
歡迎轉載,轉載請註明出處;筆者水平有限,錯誤之處歡迎指正!

一、Java虛擬機記憶體劃分

Java虛擬機記憶體區域按線程是否私有可以分為:
線程共用:方法區(含運行時常量池)、堆。

線程私有:虛擬機棧、本地方法棧、程式計數器。

下麵的圖可以幫助我們很直觀的理解JMM。

下麵圖來自programcreek,這個網站也是很有意思的網站

第二個圖來自《深入理解Java虛擬機-JVM高級特性和最佳實踐》

1.線程私有區

(1)程式計數器

線程是CPU調度的基本單位。每條線程使用一個獨立的程式計數器去記錄其正在執行的位元組碼指令地址。如果線程正在執行的是一個 Java方法,計數器記錄的是正在執行的位元組碼指令的地址;如果正在執行的是 Native 方法,則計數器的值為空。程式計數器是唯一一個沒有規定任何OutOfMemoryError
的區域。異常簡寫:OutOfMemoryError,OOM;SOF,StackOverflowError。下同。

(2)虛擬機棧

線程私有,java方法執行的模型。(創建線程)執行每個方法時會創建一個棧幀。棧幀包含:局部變數表、操作數棧、動態鏈接、方法出口。
局部變數表:基本類型(int,short,long,byte,float,double,boolean,char)和對象句柄(引用)。
異常情況:-Xss設置虛擬機棧大小,即深度(遞歸層次)當,棧深度>虛擬機最大棧深度,拋SOF;當申請棧記憶體大小不夠時,拋OOM。

(3)本地方法棧

執行Native方法,Native方法不是Java方法,由虛擬機實現,本地方法棧會拋SOF和OOM。

2.線程共用區

(1)Java堆

存放對象(實例),包含對象和數組。按GC情況分為新生代和老年代。物理記憶體可以不連續只有邏輯連續就可。垃圾收集(GC)會在之後的博客詳解。
堆相關虛擬機參數有:-Xmx:最大對容量 -Xms:最小堆容量。

異常情況:如果堆記憶體無法分配實例(對象),堆記憶體不夠時,拋OOM。

(2)方法區

線程共用,不需要物理連續記憶體,存放被JVM載入的類信息、常量、靜態變數、即時編譯的代碼。有時會也稱“永久代”。永久代已被移除,不再討論。
異常情況:方法區記憶體不足,拋OOM。

①運行時常量池
存放編譯期生成的字面常量和符號引用。拋OOM
字面常量:字元串、final常量值。
符號引用:類(介面)的全限定名稱、欄位的名稱和描述符、方法的名稱和描述符。

(3)方法區回收情況

常量池回收和對類型的卸載。
常量池回收判斷條件:沒有指向該常量(實例)的引用。
回收類型判斷條件:①類的所有所有實例都被回收;②載入該類的ClassLoader被回收;③該類的Class對象沒有任何地方被引用,無法通過反射訪問該類

二.java對象在jvm的創建和訪問定位

1.對象創建過程

(1)檢查所要new的類是否載入,沒有載入則執行類載入。類載入一般分為載入、鏈接、初始化,之後的博客我會講類載入機制。

(2)類載入完成,為對象分配記憶體。
為對象分配記憶體可以分兩種情況。

堆記憶體規整:指針碰撞,把分界指針向空閑記憶體移動一段對象記憶體大小的距離。

堆記憶體不規整:空閑列表,維護一個列表,從列表中查找足夠的記憶體保存對象(實例)。

(3)jvm將分配的記憶體初始化為零值。

(4)執行

2.對象在虛擬機(堆)的訪問定位

開發者通過操作引用(在棧上)來操作對象或實例(在堆上)。通過引用操縱對象的訪問方式有句柄訪問和直接指針訪問。
句柄訪問:堆上有句柄池,棧中的reference執行對象的句柄地址,句柄保存對象實例數據(在堆上)和類型數據(在方法區)的地址。如圖:

直接指針訪問:reference保存對象地址。如圖:

三.記憶體異常情況分析

1、java堆溢出(OOM)

不斷生成大對象,並保證不被GC回收。看下麵簡單的實例:
JVM執行參數:-verbose:gc -Xms20M -Xmx10M-XX:+PrintGCDetails -XX:SurvivorRatio=8。

/**
* VM Args:-Xms40M -Xmn10M
*/
public class OOMInHeap {
 static  class  OOMInstance{}
 public  static void  main(String[] args){
     List<OOMInstance> list=new ArrayList<>();
     while(true){
         list.add(new OOMInstance());
     }
 }
}

測試結果:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3720)
at java.base/java.util.Arrays.copyOf(Arrays.java:3689)
at java.base/java.util.ArrayList.grow(ArrayList.java:237)
at java.base/java.util.ArrayList.grow(ArrayList.java:242)
at java.base/java.util.ArrayList.add(ArrayList.java:485)
at java.base/java.util.ArrayList.add(ArrayList.java:498)
at com.zafkiel.OOMInHeap.main(OOMInHeap.java:14)

2、棧溢出(SOF/OOM)

(1).SOF
線程請求的棧深度大於虛擬機棧允許的最大深度,拋SOF。比如,沒有出口的遞歸方法調用。
測試實例:

/**
* VM Args: -Xss128K
* @author  Zafkiel
*/
public class SOFInStack {
private  int depth=1;
public  void  sof(){
    depth++;
    sof();
}
public  static  void  main(String[] args) throws  Throwable{
    SOFInStack sof=new SOFInStack();
    try{
        sof.sof();
    }catch (Throwable e){
        System.out.println("stack lenth:"+sof.depth);
        throw  e;
    }
}
}

測試結果:

Exception in thread "main" java.lang.StackOverflowError
stack lenth:7205
at com.zafkiel.SOFInStack.sof(SOFInStack.java:11)


(2).OOM
拓展棧時無法申請足夠的記憶體,則拋OOM。這種情況可以通過不停創建線程來測試。有興趣的讀者可以測試一下,註意Windows可能會假死,丟虛擬機測比較安全。

3.方法區和運行常量池溢出(OOM)

此區域可以通過不停生成類來填滿方法區,也可以通過動態類生成相關技術來實現。下麵用CGLIB動態代理來測試方法區溢出:
測試實例:

/**
 * VM Args: -XX:MetaspaceSize=10M -XX:MaxMetaspaceSize=5M
 * @author  Zafkiel
 */
public class OOMInMethodArea {
static  class  OOM{
     static int[] array=new int[1024*1024];
}
public static  void main(String[] args) throws  Throwable{
    try{
    while (true){
        Enhancer enhancer=new Enhancer();
        enhancer.setSuperclass(OOM.class);
        enhancer.setUseCache(false);
        enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o,objects));
       enhancer.create();
    }
    }catch (Throwable e){
        System.out.println("異常信息:"+e+":"+e.getMessage());
    }
}
}

測試結果(部分輸出):

異常信息:java.lang.OutOfMemoryError: Metaspace:Metaspace

這裡使用的是jdk11,永久代從jdk8之後被移到元數據區,所以JVM參數配置MetaspaceSize。

總結

對Java記憶體模型作下簡單總結:


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 關於出現 idclass mapping 運行錯誤 @IdClass 註釋通常用於定義包含複合鍵id的Class。即多個屬性的關鍵複合。 @IdClass(CountrylanguageEntityPK.class) 則CountrylanguageEntityPK如下所示: 使用註解 @IdCla ...
  • 題目: Farmer John likes to play mathematics games with his N cows. Recently, they are attracted by recursive sequences. In each turn, the cows would sta ...
  • 8、redis集群怎麼做 1、Redis集群提供了以下兩個好處1、將數據自動切分(split)到多個節點2、當集群中的某一個節點故障時,redis還可以繼續處理客戶端的請求。2、集群的方案: redis-cluster集群,採用無中心結構,每個節點保存數據和整個集群狀態,每個節點都和其他所有節點連接 ...
  • 主要內容來自中文版的同名教程 "Go語言之旅" 其目的為總結要點 包,函數和變數 包 import 語法,多個用括弧換行擴起,包之間不需要間隔符,用引號引起 go import ( "fmt" "math/rand" ) // 官方認為分組導入比多個導入更好 // 用 引用包內對象,僅有首字母大寫的 ...
  • 引言 - 一切才剛剛開始 structc 是 C 結構基礎庫. 簡單有態度. structc - https://github.com/wangzhione/structc 之前推過幾次 structc, 沒什麼效果. 這次乘著最近加班不多, 來詳細解說哈 structc 的思考初衷. 0.0 整體 ...
  • 示例: 什麼是對象  《JAVA編程思想》對於對象的定義是:將問題空間中的元素以及它們在方案空間的表示物稱作“對象”。  1. 問題空間:實際解決的問題模型;  2. 方案空間: 電腦(機器模型)。  實際的問題在電腦(機器模型)中的表示稱為對象。在上面示 ...
  • [TOC] Dubbo入門 Editor:SimpleWu Dubbo是 阿裡巴巴公司開源的一個高性能優秀的服務框架使得應用可通過高性能的 RPC 實現服務的輸出和輸入功能,可以和 Spring框架無縫集成。 背景 隨著互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分散式服務架 ...
  • 經過上一篇教程的學習,我們知道對象將它的狀態存在域中。然而,Java中也使用了“變數”這個術語。在這一篇教程中,我們將會討論它們之間的關係,以及變數命名的規則和慣例,基本數據類型以及它們的預設值和字面量。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...