jvm之虛擬機內存的各個區域(一)

全局圖
image-20200524164653762

jvm介紹

jvm位置:

image-20200621122909105

jvm體系結構:

image-20200621123008366

類加載器

類加載器負責加載class文件,class文件在文件開頭有特定的文件標示,並且ClassLoader只負責class文件的加載,至於它是否可以運行,則由Execution Engine決定 Execution Engine執行引擎負責解釋命令,提交操作系統執行

image-20200621123224067

虛擬機自帶的加載器:
• 啓動類加載器(Bootstrap)C++
• 擴展類加載器(Extension)Java
• 應用程序類加載器(AppClassLoader)java 也叫系統類加載器,加載當前應用的classpath的所有類

用戶自定義加載器 Java.lang.ClassLoader的子類,用戶可以定製類的加載方式

image-20200621123508432

PC寄存器

每個線程都有一個程序計數器,是線程私有的,就是一個指針,指向方法區中的方法字節碼(用來存儲指向下一條指令的地址,也即將
要執行的指令代碼),由執行引擎讀取下一條指令,是一個非常小的內存空間,幾乎可以忽略不記。

方法區Method Area

方法區是被所有線程共享,所有字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。簡單說,所有定
義的方法的信息都保存在該區域,此區屬於共享區間。

方法區存放內容:

• 類信息
類的版本
字段
方法
接口
• 靜態變量
• 常量
• 類信息(構造方法/接口定義)
• 運行時常量池

靜態變量+常量+類信息(構造方法/接口定義)+運行時常量池存在方法區中

運行時常量池 是方法區的一部分,用於存放編譯期生成的各種字面量和符號
引用,這部分內容將在類加載後存放到常量池中

方法區(Method Area),是各個線程共享的內存區域,它用於存儲虛擬機加載的:類信息+普通常量+靜態常量+編譯器編譯後的代碼等等,雖然JVM規範將方法區描述爲堆的一個邏輯部分,但它卻還有一個別名叫做Non-Heap(非堆),目的就是要和堆分開。
對於HotSpot虛擬機,很多開發者習慣將方法區稱之爲“永久代(ParmanentGen)” ,但嚴格本質上說兩者不同,或者說使用永久代來實現方法區而已,永久代是方法區(相當於是一個接口interface)的一個實現,jdk1.7的版本中,已經將原本放在永久代的
字符串常量池移走。
常量池(Constant Pool)是方法區的一部分,Class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,這部分內容將在類加載後進入方法區的運行時常量池中存放。

棧Stack

棧也叫棧內存,主管Java程序的運行,是在線程創建時創建,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放, 對於棧來說不存在垃圾回收問題,只要線程一結束該棧就Over,生命週期和線程一致,是線程私有的。

8種基本類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配。
棧區

棧存儲內容:
• 局部變量表:輸入參數和輸出參數以及方法內的變量類型;局部變量
表在編譯期間完成分配,當進入一個方法時,這個方法在幀中分配
多少內存是固定的
• 棧操作(Operand Stack):記錄出棧、入棧的操作;
• 動態鏈接
• 方法出口

棧溢出
StackOverflowError,OutOfMemory

image-20200621125017129

圖示在一個棧中有兩個棧幀:棧幀 2是最先被調用的方法,先入棧,然後方法 2 又調用了方法1,棧幀 1處於棧頂的位置,
棧幀 2 處於棧底,執行完畢後,依次彈出棧幀 1和棧幀 2,線程結束,棧釋放。
每執行一個方法都會產生一個棧幀,保存到棧( 後進先出) 的頂部,頂部棧就是當前的方法,該方法執行完畢 後會自動將此棧幀出棧。

Native Interface:本地接口的作用是融合不同的編程語言爲 Java 所用

Native Method Stack:它的具體做法是Native Method Stack中登記native方法,在Execution
Engine 執行時加載本地方法庫

堆Heap

一個JVM實例只存在一個堆內存,堆內存的大小是可以調節的。類加載器讀取了類文件後,需要把類、方法、
常變量放到堆內存中,保存所有引用類型的真實信息,以方便執行器執行,

堆內存分爲三部分:

  • Young Generation Space 新生區 Young/New

  • Tenure generation space 養老區 Old/ Tenure

  • Permanent Space 永久區 Perm

Heap堆( Java8)
一個JVM實例只存在一個堆內存,堆內存的大小是可以調節的。類加載器讀取了類文件後,需要把類、方法、常變量放到堆內存中,保
存所有引用類型的真實信息,以方便執行器執行。

堆內存 邏輯上 分爲三部分:新生+ + 養老+ + 方法區

image-20200621160022651

方法區
永久存儲區是一個常駐內存區域,用於存放JDK自身所攜帶的 Class,Interface的元數據,也就是說它存儲的是運行環境必須的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉 JVM 纔會釋放此區域所佔用的內存。
如果出現java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機對永久代Perm內存設置不夠。一般出現這種情況,都是程序啓動需要加載大量的第三方jar包。例如:在一個Tomcat下部署了太多的應用。或者大量動態反射生成的類不斷被加載,最終導致Perm區被佔滿。
Jdk1.6及之前: 有永久代, 常量池1.6在方法區
Jdk1.7: 有永久代,但已經逐步“去永久代”,常量池1.7在堆
Jdk1.8及之後: 無永久代,常量池1.8在元空間

方法區(Method Area),是各個線程共享的內存區域,它用於存儲虛擬機加載的:
類信息+普通常量+靜態常量+編譯器編譯後的代碼等等,雖然JVM規範將方法區描述爲堆
的一個邏輯部分,但它卻還有一個別名叫做Non-Heap(非堆),目的就是要和堆分開。
對於HotSpot虛擬機,很多開發者習慣將方法區稱之爲“永久代(Parmanent
Gen)” ,但嚴格本質上說兩者不同,或者說使用永久代來實現方法區而已,永久代是方
法區(相當於是一個接口interface)的一個實現,jdk1.7的版本中,已經將原本放在永久代的
字符串常量池移走。
常量池(Constant Pool)是方法區的一部分,Class文件除了有類的版本、字段、
方法、接口等描述信息外,還有一項信息就是常量池,這部分內容將在類加載後進入方
法區的運行時常量池中存放。

java 7

image-20200621160733580

JDK 1.8之後將最初的永久代取消了,由元空間取代

image-20200621160619979

堆棧方法區的關係

image-20200621130613546

HotSpot 是使用指針的方式來訪問對象:
Java 堆中會存放訪問類元數據的地址,
reference 存儲的就直接是對象的地址

image-20200621155557823

java堆中的對象分配 佈局 和訪問

對象分配

對象創建:

image-20200621192254140

給對象分配內存:

• 指針碰撞
• 空間列表

解決線程安全性問題:

• 線程同步
• 本地線程分配緩衝(TLAB)

對象內存佈局

對象的結構:

  • Header(對象頭)

    • 自身運行時數據(Mark Word)

      • 哈希值
        GC分代年齡
        鎖狀態標誌
        線程持有鎖
        偏向線程ID
        偏向時間戳

      • 類型指針

      • 數組長度(只有數組對象纔有)

  • InstanceData
    相同寬度的數據分配到一起(long,double)

  • Padding(對齊填充)
    8個字節的整數倍

Hotspot 虛擬機對象頭 Mark Word

image-20200621192816936

對象的訪問定位

• 使用句柄
• 直接指針

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章