天天看點

深入了解JVM--深入了解虛拟機之Java記憶體區域概述

深入了解JVM--深入了解虛拟機之Java記憶體區域

  • 概述
    • Java程式執行流程
    • 運作時資料區域

概述

對于Java程式員來說,在虛拟機自動記憶體管理機制下,不再需要像C/C++程式開發程式員這樣為内一個new 操作去寫對應的delete/free操作,不容易出現記憶體洩漏和記憶體溢出問題。正是因為Java程式員把記憶體控制權利交給Java虛拟機,一旦出現記憶體洩漏和溢出方面的問題,如果不了解虛拟機是怎樣使用記憶體的,那麼排查錯誤将會是一個非常艱巨的任務。

Java程式執行流程

首先,我們先來回顧一下java的基本開發模式,我們知道,我們寫的所有的java 程式都儲存在 * .java 的檔案中,即我們的源代碼,但是呢,這些源代碼,必須經過javac.exe指令将其編譯成 *.class 檔案,而後利用 java.exe 指令在 JVM 程序中中解釋此程式。

但是在這裡流程中,又有自己的過程,如下圖:

深入了解JVM--深入了解虛拟機之Java記憶體區域概述

實際上,當JVM将所需要的 .class 檔案将所需要的 .class 檔案加載到 JVM 程序之中,那麼這個過程,我們需要一個類加載器(ClassLoad),類加載器的好處在于:可以随定指定 *.class 檔案所在的路徑。

JVM:java虛拟機,所有的程式都要求運作在JVM上,是因為考慮到了可移植性問題 ,但如果真正去執行程式,無法離開作業系統的支援。

Execution Engine執行引擎在執行Java代碼時候可能會有 解釋執行和編譯執行 兩種選擇,也可能兩者兼備,甚至還可能會包含幾個不同級别的編譯器執行引擎。

在 java 中可以使用 native 實作 本地 C 函數的調用,Native Interface,但是這些都是屬于程式的輔助手段,而真正的程式運作都在“運作時資料區”之中。

運作時資料區域

Java虛拟機在執行Java程式的過程中會把它管理的記憶體劃分成若幹個不同的資料區域。

深入了解JVM--深入了解虛拟機之Java記憶體區域概述
  1. 程式計數器

    程式計數器是一塊較小的記憶體空間,可以看作是目前線程所執行的位元組碼的行号訓示器。位元組碼解釋器工作時通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢複等功能都需要依賴這個計數器來完。

    另外,為了線程切換後能恢複到正确的執行位置,每條線程都需要有一個獨立的程式計數器,各線程之間計數器互不影響,獨立存儲,我們稱這類記憶體區域為“線程私有”的記憶體。

  2. Java虛拟機棧

    與程式計數器一樣,Java虛拟機棧也是線程私有的,它的生命周期和線程相同,描述的是Java方法執行的記憶體模型。

    Java記憶體可以粗糙的區分為堆記憶體(Heap)和棧記憶體(Stack),其中棧就是現在說的虛拟機棧,或者說是虛拟機棧中局部變量表部分。

深入了解JVM--深入了解虛拟機之Java記憶體區域概述

我們在java JVM 中用棧幀(Stack Frame)來定義棧的資料,每一個棧幀表示每個可能執行的方法。

棧幀中則包含了:局部變量表,操作樹棧,指向運作時常量池的引用,方法傳回位址和動态連結

  • 局部變量表主要存放了編譯器可知的各種資料類型、對象引用。
  • 操作樹棧(Operand Stack):表達式計算在棧中完成;
  • 指向目前方法所屬的類的運作時常量池的引用(Reference to runtime constant pool):引用其他類的常量或者使用String 池中的字元串;
  • 方法傳回位址(Return Address):方法執行完後需要傳回調用此方法的位置,是以需要再棧幀中儲存方法傳回位址;
  1. 本地方法棧

    和虛拟機棧所發揮的作用非常相似,差別是: 虛拟機棧為虛拟機執行Java方法 (也就是位元組碼)服務,而本地方法棧則為虛拟機使用到的Native方法服務。

  2. Java虛拟機所管理的記憶體中最大的一塊,Java堆是所有線程共享的一塊記憶體區域,在虛拟機啟動時建立。此記憶體區域的唯一目的就是存放對象執行個體,幾乎所有的對象執行個體以及數組都在這裡配置設定記憶體。Java堆是垃圾收集器管理的主要區域,是以也被稱作GC堆(Garbage Collected Heap).從垃圾回收的角度,由于現在收集器基本都采用分代垃圾收集算法,是以Java堆還可以細分為:新生代和老年代:在細緻一點有:Eden空間、From Survivor、To Survivor、Old Gen空間等。進一步劃分的目的是更好地回收記憶體,或者更快地配置設定記憶體。

    深入了解JVM--深入了解虛拟機之Java記憶體區域概述
    深入了解JVM--深入了解虛拟機之Java記憶體區域概述

    一定要記住在 JDK1.8 之後将最初的永久代記憶體空間取消了,以下為1.8之前的記憶體空間組成:

    取消永久代的目的:是為了将 HotSpot與JRockit 兩個虛拟機标準聯合成一個。)

    在整個的JVM堆記憶體之中實際上将記憶體分為三塊:

    年輕代:新對象和沒達到一定年齡的對象都在年輕代;

    老年代:被長時間使用的對象,老年代的記憶體空間比年輕代更大

    元空間:像一些方法中的操作臨時對象等,直接使用實體記憶體;

    最初的永久代是需要在JVM堆記憶體裡面進行劃分;

  3. 方法區

    方法區與Java堆一樣,是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即使編譯器編譯後的代碼等資料。

    HotSpot虛拟機中方法區也常被稱為 “永久代”,本質上兩者并不等價。僅僅是因為HotSpot虛拟機設計團隊用永久代來實作方法區而已,這樣HotSpot虛拟機的垃圾收集器就可以像管理Java堆一樣管理這部分記憶體了。但是這并不是一個好主意,因為這樣更容易遇到記憶體溢出問題。

    相對而言,垃圾收集行為在這個區域是比較出現的,但并非資料進入方法區後就“永久存在”了。

  4. 運作時常量池

    運作時常量池是方法區的一部分。Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有常量池資訊(用于存放編譯期生成的各種字面量和符号引用)

  5. 直接記憶體

    直接記憶體并不是虛拟機運作時資料區的一部分,也不是虛拟機規範中定義的記憶體區域,但是這部分記憶體也被頻繁地使用。而且也可能導緻OutOfMemoryError異常出現。

    JDK1.4中新加入的NIO(New Input/Output)類,引入了一種基于通道(Channel) 與緩存區(Buffer) 的I/O方式,它可以直接使用Native函數庫直接配置設定堆外記憶體,然後通過一個存儲在java堆中的DirectByteBuffer對象作為這塊記憶體的引用進行操作。這樣就能在一些場景中顯著提高性能,因為避免了在Java堆和Native堆之間來回複制資料。

    本機直接記憶體的配置設定不會收到Java堆的限制,但是,既然是記憶體就會受到本機總記憶體大小以及處理器尋址空間的限制。

這裡是引用

https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247483910&idx=1&sn=246f39051a85fc312577499691fba89f&chksm=fd985467caefdd71f9a7c275952be34484b14f9e092723c19bd4ef557c324169ed084f868bdb#rd

https://blog.csdn.net/qq_34707744/article/details/79278169