91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM內存結構劃分的示例分析

發布時間:2021-08-23 13:58:24 來源:億速云 閱讀:120 作者:小新 欄目:編程語言

這篇文章主要介紹JVM內存結構劃分的示例分析,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

數據區域劃分

運行時內存區域劃分:程序計數器、虛擬機棧、本地方法棧、堆、方法區

程序計數器

  • 線程私有

  • 通過寄存器實現

  • 不會存在運行溢出

當前線程所執行的行號指示器,記住下一條JVM指令的執行地址

虛擬機棧

  • 垃圾回收不涉及棧內存

  • 棧內存是線程私有的,可以理解為線程運行需要的內存空間

  • 棧由棧幀組成,每個棧幀代表一個方法執行時需要的內存(參數,局部變量,返回地址)

  • 每個線程只能有一個活動棧幀,對應著當前正在執行的那個方法

棧內存分配過大只能支撐一定的遞歸調用,并不會影響運行速度,還可能減少線程數量(因為物理內存是一定的)

本地方法棧

為運行本地方法時分配的內存(HotSpot把虛擬機棧和本地方法棧合二為一了)


  •  

  • 有垃圾回收機制

  • 線程共享,需要考慮線程安全問題

  • 存儲的都是對象的實例(通過new關鍵字創建的對象)

  • 從內存分配的角度來說:堆中可以劃分出多個線程私有的分配緩沖區(TLAB),以提升對象分配時的效率

  • Java堆可以處于物理上不連續的內存空間,但在邏輯上應該視為連續的(但是對于比如數組這種大對象,可能會要求連續的內存空間)

方法區

  • 線程共享區

  • 存儲已被虛擬機加載的類型信息,常量,靜態變量,即時編譯器編譯后的代碼緩存

  • 在虛擬機啟動時被創建,邏輯上屬于堆的一部分(不同JVM實現的方式不同)

    • JDK1.6使用永久代(PerGen)作為方法區的實現

    • JDK1.8使用元空間(Metaspace)對方法區進行實現(包含Class ClassLoader 常量池三個部分,放在直接內存中)StringTable放在堆中(有助于垃圾回收管理)

使用場景:如Spring Mybatis使用的動態加載

運行時常量池

運行時常量池是方法區的一部分

二進制字節碼內容:類基本信息\常量池表\類方法定義,包含了虛擬機指令

其中,常量池表中存放編譯期間生成的各種字面量(比如各種基本數據類型)與符號引用(比如,類名\方法名\參數類型),這部分內容將在類加載后存放到方法區的運行時常量池中,并把符號地址變為真實地址

StringTable

類似于hashTable結構,不能自動擴容

常量池中的字符串只是符號,第一次使用時才變為對象

利用串池機制,避免重復創建字符對象

案例

  • 字符串拼接的原理是編譯期優化

  • 字符串拼接原理是StringBuilder(JDK1.8)

  • 使用intern()方法,主動將串池中還沒有的字符串對象放入串池

// StringTable [ "a", "b" ,"ab" ] hashtable 結構,不能擴容
public class Demo1_22 {
  // 常量池中的信息,都會被加載到運行時常量池中, 這時 a b ab 都是常量池中的符號,還沒有變為 java 字符串對象
  // ldc #2 會把 a 符號變為 "a" 字符串對象
  // ldc #3 會把 b 符號變為 "b" 字符串對象
  // ldc #4 會把 ab 符號變為 "ab" 字符串對象

  public static void main(String[] args) {
    String s1 = "a"; // 懶惰的
    String s2 = "b";
    String s3 = "ab";
    String s4 = s1 + s2; // new StringBuilder().append("a").append("b").toString() new String("ab")
    String s5 = "a" + "b"; // javac 在編譯期間的優化,結果已經在編譯期確定為ab

    System.out.println(s3 == s5);
  }
}

JDK1.7以后,利用intern()方法,會將字符串對象嘗試放入串池,如果有則并不會放入,如果沒有則放入串池, 會把串池中的對象返回;而JDK1.6調用intern()方法,是將對象拷貝一份到串池中,指向堆中的對象本身引用并不不改變

public class Demo1_23 {

  // ["ab", "a", "b"]
  public static void main(String[] args) {
    demo1();
    demo2();
  }

  static void demo1() {
    // 串池中事前沒有"ab",intern()之后,s返回的是串池中的對象
    String s = new String("a") + new String("b");

    String s1 = s.intern();

    System.out.println(s == "ab"); // true
    System.out.println(s1 == "ab");   //true
  }

  static void demo2() {
    // 串池中事前已有"ab",s返回的仍是堆中的對象
    String x = "ab";
    String s = new String("a") + new String("b");

    // 堆 new String("a")  new String("b") new String("ab")
    String s2 = s.intern(); // 將這個字符串對象嘗試放入串池,如果有則并不會放入,如果沒有則放入串池, 會把串池中的對象返回

    System.out.println( s2 == x);  // true
    System.out.println( s == x );  //false
  }
}

位置

JDK1.6時,StringTable放在元空間內,屬于永久代的位置,但是StringTable占用內存容易觸發full gc耗時較久;JDK1.7以后將StringTable放在堆內存中,隨著內存占用增大首先觸發minor gc,耗時較短.

直接內存

使用Native函數直接分配堆外內存,然后通過Java堆里的DirectByteBuffer對象作為引用對這塊內存的引用進行操作.
原理說明:

使用Unsafe對象完成直接內存的分配和回收,回收時需要主動調用freeMemory方法

ByteBuffer的實現類內部使用了Cleaner(虛引用)來監測ByteBuffer(BB)對象,一旦BB對象被垃圾回收,會有ReferenceHandler線程通過Cleaner方法調用freeMemory來釋放內存

創建新對象說明

HotSpot虛擬機在Java堆中對象分配、布局和訪問的過程

對象的創建

new字節碼指令

虛擬機遇到new字節碼指令時,首先檢查能否在常量池中定位到一個類的符號引用,并檢查該符號引用的來是否已被加載、解析和初始化。如果沒有,則執行相應的類加載過程。
類加載檢查后,虛擬機為新生對象分配內存

內存分配

對象所需的內存大小在類加載過程中可以確定,在Java虛擬機中為對象劃分內存時有兩種方式:指針碰撞、空閑列表
指針碰撞: 利用一個指針作為已用內存和未用內存的分界點的指示器,內存分配就僅僅是指針的移動。優點在于不會造成內存碎片化,但是速度較慢

空閑列表:虛擬機維護一個內存使用記錄表,使用時,從空閑的內存區域直接劃分一塊足夠大的空間給對象實例。

內存分配的線程安全問題

劃分可用空間后仍要考慮并發情況下對內存的使用,有兩種方式解決內存沖突的問題:CAS配上失敗重試、TLAB本地線程分配緩沖
TLAB:把內存分配的動作按照線程劃分在不同的空間之中進行,即每個線程在Java堆中預先分配了一小塊內存空間

對象的內存布局

對象在堆內存中的布局可以劃分為三個部分:對象頭、實例數據、對齊填充

對象頭

對象頭中包含兩類信息:Mark Word、類型指針

Mark Word

存儲對象自身運行時數據,考慮到虛擬機的空間效率,被設計成一個動態定義的數據結構,即根據對象的狀態復用自己的存儲空間(數據長度在32位和64位虛擬機上分別為32個比特和64個比特)

類型指針

對象中指向它類型元數據的指針,Java虛擬機通過這個指針來確定該對象是哪個類的實例(不是所有虛擬機都必須在對象數據上保留類型指針)此外,如果對象是一個數組,對象頭中還必須擁有一塊記錄數據長度的數據

實例數據

即程序代碼里定義的各種類型的字段內容,包括從父類繼承的或子類中定義的字段。各類數據存儲是按照一定順序的(long/double、ints...),而寬度相同的字段總是被分配到一起存放,所以父類中定義的變量可能會出現在子類之前。

對齊填充

占位符,無特殊意義

HotSpot虛擬機的自動內存管理系統要求對象的起始地址必須是8字節的整倍數,若有些對象的對象頭和示例數據內存設計不是8的倍數,則需要利用占位符來進行填充。

對象的訪問定位

Java程序通過reference數據操作對上的具體對象,主流的訪問方式有兩種:句柄、直接指針

句柄

Java堆中可能劃分出一塊內存作為句柄池。reference中存儲對象的句柄地址,句柄中包含對象的實例數據和類型數據的具體地址信息。

直接指針

Java堆中對象的布局需要考慮如何放置類型數據的相關信息(如訪問信息)。reference中存儲的直接就是對象地址,如果只訪問對象本身,就不要多一次間接訪問的開銷

優缺點

使用句柄訪問, reference數據只需關乎句柄地址,當對象被回收或移動后只需改變句柄中的實例數據指針,而reference本身不用修改
使用直接指針省去了一次指針定位的時間開銷,速度更快,由于Java中對象的訪問相當頻繁,所以效果可觀。
HotSpot使用直接指針的方式

以上是“JVM內存結構劃分的示例分析”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

jvm
AI

谢通门县| 大石桥市| 宜城市| 班玛县| 麦盖提县| 丰原市| 甘谷县| 册亨县| 嘉禾县| 尉氏县| 承德县| 宜昌市| 五大连池市| 赤峰市| 咸丰县| 离岛区| 庆安县| 新源县| 遂平县| 都兰县| 漯河市| 玉树县| 永济市| 开封县| 宝兴县| 神池县| 调兵山市| 钟山县| 贡山| 清徐县| 台前县| 浦江县| 聊城市| 闽清县| 泰宁县| 满洲里市| 吴堡县| 疏附县| 云浮市| 灵宝市| 恭城|