一文讀懂 -Java虛擬機的內存結構(圖詳解)

你好我是辰兮,很高興與你分享近期學習總結,本篇是學習了嗶哩嗶哩上面JVM底層原理後的小結,學習完對Java內存結構有了新的見解,爲了更直觀的感受其模型,找了很多的圖片,希望對你們理解學習有幫助。



0、序言(運行時數據區)

JVM中的堆啊、棧啊、方法區什麼的,是Java虛擬機的內存結構Java程序啓動後,會初始化這些內存的數據。

Java 虛擬機在執行 Java 程序的過程中會把它管理的內存劃分爲若干個不同的數據區域。每個區域都有各自的作用。

分析 JVM 內存結構,主要就是分析 JVM 運行時數據存儲區域。JVM 的運行時數據區主要包括:方法區程序計數器等。

JVM 的優化問題主要在線程共享的數據區中:堆、方法區。

在這裏插入圖片描述

內存結構就是上圖中內存空間這些東西,而Java內存模型,完全是另外的一個東西。


Java中的內存分配:

Java程序在運行時,需要在內存中的分配空間。爲了提高運算效率,就對數據進行了不同空間的劃分,因爲每一片區域都有特定的處理數據方式和內存管理方式。

具體劃分爲如下5個內存空間:

  • 棧:存放局部變量
  • 堆:存放所有new出來的東西
  • 方法區:被虛擬機加載的類信息、常量、靜態常量等。
  • 程序計數器(和系統相關)
  • 本地方法棧
    在這裏插入圖片描述

1、程序計數器(Program Counter Register)

程序計數器是一塊很小的內存空間,它是線程私有的,可以認作爲當前線程的行號指示器

一個線程的執行,是通過字節碼解釋器改變當前線程的計數器的值,來獲取下一條需要執行的字節碼指令,從而確保線程的正確執行。
在這裏插入圖片描述


爲什麼需要程序計數器 ?

我們知道對於一個處理器(如果是多核cpu那就是一核),在一個確定的時刻都只會執行一條線程中的指令,一條線程中有多個指令,爲了線程切換可以恢復到正確執行位置,每個線程都需有獨立的一個程序計數器不同線程之間的程序計數器互不影響,獨立存儲。

注意:如果線程執行的是個java方法,那麼計數器記錄虛擬機字節碼指令的地址。如果爲native【底層方法】,那麼計數器爲空。這塊內存區域是虛擬機規範中唯一沒有OutOfMemoryError的區域。


2、虛擬機棧(Java Stack)

又叫Java棧,就是我們平常說的棧

虛擬機棧也是線程私有的,生命週期與程序計數器相同,就是我們平時說的棧,棧描述的是Java方法執行的內存模型。

虛擬機棧-過程詳情描述

每創建一個線程,虛擬機就會爲這個線程創建一個虛擬機棧
②虛擬機棧表示Java方法執行的內存模型,每調用一個方法,就會生成一個棧幀(Stack Frame)用於存儲方法的本地變量表、操作棧、方法出口等信息,當這個方法執行完後,就會彈出相應的棧幀。

在這裏插入圖片描述

如果請求的棧的深度過大,虛擬機可能會拋出StackOverflowError異常,如果虛擬機的實現中允許虛擬機棧動態擴展,當內存不足以擴展棧的時候,會拋出OutOfMemoryError異常。

在這裏插入圖片描述
棧幀(Stack Frame)

在這裏插入圖片描述
棧幀分爲三部分局部變量區(Local Variables)、操作數棧(Operand Stack)和幀數據區(Frame Data)。

  • 局部變量區(Loca Variables):局部變量區被組織一個一個從0開始的字數組,byte、short、char在存儲前被轉換爲int,boolean也被轉換爲int,0表示false,非0表示true,long和double佔據兩個字長。
  • 操作數棧(Operand Stack):操作數棧也被組織爲一個字數組,但不同於局部變量區,它不是通過數組下標訪問的,而是能過棧的Push和Pop操作,前一個操作Push進的數據可以被下一個操作Pop出來使用。
  • 幀數據區(Frame Data)這部分的作用主要有三部分:
    • 常量池中數據的解析
    • 方法執行完後處理方法返回,恢復調用方現場
    • 方法執行過程中拋出異常時的異常處理,存儲有一個異常表,當出現異常時虛擬機查找相應的異常表看是否有對應的Catch語句,如果沒有就拋出異常終止這個方法調用

3、本地方法棧(Native Method Stack)

與虛擬機棧類似,只是是執行本地方法時使用的

  • (本地方法棧則爲 JVM 使用到的 Native 方法服務。)

4、方法區(Method Area)

用於存儲已被虛擬機加載的類型信息、常量、靜態變量、即時編譯後的代碼等信息。

方法區是線程間共享的,當兩個線程同時需要加載一個類型時,只有一個類會請求ClassLoader加載,另一個線程會等待。
在這裏插入圖片描述
小結:

  • 靜態變量+常量+類信息(版本、方法、字段等)+運行時常量池存在方法區中

拓展內容 - 常量池分類

  • 常量池大體可以分爲:靜態常量池,運行時常量池。

靜態常量池 存在於class文件中,比如經常使用的javap -verbose中,常量池總是在最前面

運行時常量池:就是在class文件被加載進了內存之後,常量池保存在了方法區中,通常說的常量池 指的是運行時常量池。所以呢,討論的都是運行時常量池

在這裏插入圖片描述


5、堆(Heap)

虛擬機中用於存放對象與數組實例的地方,垃圾回收的主要區域就是這裏(還可能有方法區)。

Java 中的堆是 JVM 所管理的最大的一塊內存空間,主要用於存放各種類的實例對象

在 Java 中,堆被劃分成兩個不同的區域:新生代 ( Young )、老年代 ( Old )。

新生代又可能分爲Eden區,From Survivor區和To Survivor區,主要是爲了垃圾回收。所有的線程共享Java堆,在這裏還可以劃分線程私有的緩衝區(Thread Local Allocation Buffer,TLAB)。
在這裏插入圖片描述

堆的大小可以通過參數 –Xms、-Xmx 來指定。

Java堆只要求邏輯上是連續的,在物理空間上可以不連續。


什麼是新生代 ?

新生代(Young Generation)主要是用來存放新生的對象。

新生代又被進一步劃分爲Eden區(伊甸園區)和Survivor區(倖存區,包含空間相等的S0、S1區,或者說From、To區,沒有先後順序,是Copying算法的需要)。

大多數情況下,java中新建的對象都是在新生代上分配的,通過Copying算法來進行分配內存和垃圾回收。

什麼是老生代 ?

老生代(Old Generation)主要存放應用程序中生命週期長的內存對象。

在新生代中經過多次垃圾回收仍然存活的對象,會被存放到老生代中。老生代通過標記/整理算法來清理無用內存。


在這裏插入圖片描述


The best investment is to invest in yourself

在這裏插入圖片描述

2020.05.19 記錄辰兮的第67篇博客

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