JVM的內存結構詳解

1、JVM基礎知識

一、什麼是JVM

JVM是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用於計算設備的規範,它是一個虛構出來的計算機,是經過在實際的計算機上仿真模擬各類計算機功能來實現的。Java虛擬機包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆和一個存儲方法域。java

二、JRE/JDK/JVM是什麼關係

JRE(JavaRuntimeEnvironment,Java運行環境),也就是Java平臺。全部的Java程序都要在JRE下才能運行。普通用戶只須要運行已開發好的Java程序,安裝JRE便可。數組

JDK(Java Development Kit)是程序開發者用來來編譯、調試Java程序用的開發工具包。JDK的工具也是Java程序,也須要JRE才能運行。爲了保持JDK的獨立性和完整性,在JDK的安裝過程當中,JRE也是 安裝的一部分。因此,在JDK的安裝目錄下有一個名爲jre的目錄,用於存放JRE文件。緩存

JVM(JavaVirtualMachine,Java虛擬機)是JRE的一部分。它是一個虛構出來的計算機,是經過在實際的計算機上仿真模擬各類計算機功能來實現的。JVM有本身完善的硬件架構,如處理器、堆棧、寄存器等,還具備相應的指令系統。Java語言最重要的特色就是跨平臺運行。使用JVM就是爲了支持與操做系統無關,實現跨平臺。安全

三、JVM執行程序的過程

  • 加載.class文件
  • 管理並分配內存
  • 執行垃圾回收

JRE(java運行時環境)是JVM構造的java程序的運行環境,也是Java程序運行的環境,可是一個操做系統的一個應用程序一個進程也有他本身的運行的生命週期,也有本身的代碼和數據空間。JVM在整個jdk中處於最底層,負責於操做系統的交互,用來屏蔽操做系統環境,提供一個完整的Java運行環境,所以也就虛擬計算機。操做系統裝入JVM是經過jdk中Java.exe來完成,經過下面4步來完成JVM環境:多線程

  • 建立JVM裝載環境和配置
  • 裝載JVM.dll
  • 初始化JVM.dll並掛界到JNIENV(JNI調用接口)實例
  • 調用JNIEnv實例裝載並處理class類

四、JVM的生命週期

(1)JVM實例對應了一個獨立運行的java程序它是進程級別架構

  • 啓動。啓動一個Java程序時,一個JVM實例就產生了,任何一個擁有 public static void main(String[]args)函數的class均可以做爲JVM實例運行的起點。
  • 運行。main()做爲該程序初始線程的起點,任何其餘線程均由該線程啓動。JVM內部有兩種線程:守護線程和非守護線程,main()屬於非守護線程,守護線程一般由JVM本身使用,java程序也能夠代表本身建立的線程是守護線程。
  • 消亡。當程序中的全部非守護線程都終止時,JVM才退出;若安全管理器容許,程序也可使用Runtime類或者System.exit()來退出。

(2)JVM執行引擎實例則對應了屬於用戶運行程序的線程它是線程級別的函數

五、JVM垃圾回收

GC (Garbage Collection)的基本原理:將內存中再也不被使用的對象進行回收,GC中用於回收的方法稱爲收集器,因爲GC須要消耗一些資源和時間,Java在對對象的生命週期特徵進行分析後,按照新生代、老年代的方式來對對象進行收集,以儘量的縮短GC對應用形成的暫停。工具

  • 對新生代的對象的收集稱爲minor GC
  • 對舊生代的對象的收集稱爲Full GC
  • 程序中主動調用System.gc()強制執行的GC爲Full GC

不一樣的對象引用類型,GC會採用不一樣的方法進行回收,JVM對象的引用分爲了四種類型:性能

  • 強引用:默認狀況下,對象採用的均爲強引用(這個對象的實例沒有其餘對象引用,GC時纔會被回收)
  • 軟引用:軟引用是Java中提供的一種比較適合於緩存場景的應用(只有在內存不夠用的狀況下才會被GC)
  • 弱引用:在GC時必定會被GC回收
  • 虛引用:因爲虛引用只是用來得知對象是否被GC

Java程序具體執行的過程:開發工具

  • 首先Java源代碼文件(.java文件)會被Java編譯器(javac.exe)編譯爲字節碼文件(.class文件)。
  • 而後由JVM中的類加載器(ClassLoader)加載各個類的字節碼文件,加載完畢以後,交由JVM執行引擎執行。在整個程序執行過程當中,JVM會用一段空間來存儲程序執行期間須要用到的數據和相關信息,這段空間通常被稱做爲Runtime Data Area(運行時數據區),也就是 咱們常說的JVM內存。所以,在Java中咱們經常說到的內存管理就是針對這段空間進行管理(如何分配和回收內存空間)。

2、運行時數據區的組成/JVM內存結構/Java內存空間分類

JVM在Java程序運行時把它所管理的內存劃分爲幾個不一樣的數據區域:程序計數器(Program Counter Register)、虛擬機棧(VM Stack)、本地方法棧(Native Method Stack)、 方法區(Method Area)、堆(Heap)。

如上圖所示,方法區和堆爲線程共享區,虛擬機棧、本地方法棧和程序計數器爲線程獨佔區。在JVM規範中雖然規定了程序在執行期間運行時數據區應該包括這幾部分,可是至於具體如何實現並無作出規定,不一樣的虛擬機廠商能夠有不一樣的實現方式。

方法區是虛擬機規範中對運行時數據區劃分的一個內存區域,不一樣的虛擬機廠商能夠有不一樣的實現,而HotSpot虛擬機以永久代來實現方法區,因此方法區是一個規範,而永久代則是其中的一種實現方式。

(一)程序計數器/寄存器

程序計數器(Program Counter Register),也有稱做爲PC寄存器。程序計數器是一塊較小 的空間,它能夠看做是當前線程所執行的字節碼的行號指示器。

若是線程執行的是java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址,若是正在執行的是native方法,這個計數器的值爲undefined。

JVM的多線程是經過線程輪流切換並分配CPU執行時間片的方式來實現的,任何一個時刻,一個CPU都只會執行一條線程中的指令。爲了保證線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器,各線程間的程序計數器獨立存儲,互不影響。

此區域是惟一一個在java虛擬機規範中沒有規定任何OutOfMemoryError狀況的區域,由於程序計數器是由虛擬機內部維護的,不須要開發者進行操做。

(二)Java虛擬機棧

描述的是java 方法執行的內存模型:每一個方法被執行的時候 都會建立一個「棧幀」用於存儲局部變量表(包括參數)、操做棧、方法出口等信息。每一個方法被調用到執行完的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。聲明週期與線程相同,是線程私有的。

局部變量表存放了編譯器可知的各類基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(引用指針,並不是對象自己),其中64位長度的long和double類型的數據會佔用2個局部變量的空間,其他數據類型只佔1個。局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在棧幀中分配多大的局部變量是徹底肯定的,在運行期間棧幀不會改變局部變量表的大小空間。

(三)本地方法棧

與虛擬機棧基本相似,區別在於虛擬機棧爲虛擬機執行的java方法服務,而本地方法棧則是爲Native方法服務。HotSpot虛擬機不區分虛擬機棧和本地方法棧,二者是一塊的。與虛擬機棧同樣,本地方法棧也會拋StackOverflowError和OutOfMemoryError異常。

(四)堆

JVM管理的最大的一塊內存區域,存放着對象的實例,是線程共享區。

堆是垃圾收集器管理的主要區域,所以也被稱爲「GC堆」。

JAVA堆的分類:

  • 從內存回收的角度上看,可分爲新生代(Eden空間,From Survivor空間、To Survivor空間)及老年代(Tenured Gen)

  • 從內存分配的角度上看,爲了解決分配內存時的線程安全性問題,線程共享的JAVA堆中可能劃分出多個線程私有的分配緩衝區(TLAB)

JAVA堆能夠處於物理上不連續的內存空間中,只要邏輯上是連續的便可。

可經過參數 -Xmx -Xms 來指定運行時堆內存的大小,堆內存空間不足也會拋OutOfMemoryError異常。

(五)方法區

也稱」永久代」 、「非堆」,它用於存儲虛擬機加載的類信息、常量、靜態變量、是各個線程共享的內存區域。默認最小值爲16MB,最大值爲64MB,能夠經過-XX:PermSize 和 -XX:MaxPermSize 參數限制方法區的大小。

運行時常量池:是方法區的一部分,其中的主要內容來自於JVM對Class的加載。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用於存放編譯器生成的各類符號引用,這部份內容將在類加載後放到方法區的運行時常量池中。

直接內存(Direct Memory)

直接內存並非虛擬機運行時數據區的一部分,也不是Java虛擬機規範中定義的內存區域,它直接從操做系統中分配,所以不受Java堆大小的限制,可是會受到本機總內存的大小及處理器尋址空間的限制,所以它也可能致使OutOfMemoryError異常出現。在JDK1.4中新引入了NIO機制,它是一種基於通道與緩衝區的新I/O方式,能夠直接從操做系統中分配直接內存,即在堆外分配內存,這樣能在一些場景中提升性能,由於避免了在Java堆和Native堆中來回複製數據。

3、總結

程序計數器/寄存器: 咱們在程序中沒法控制

堆:存放用new產生的數據(對象實例和數組)

棧:基本數據類型和對象的引用, 對象自己是不存放在棧中的,而是存放在堆中

方法區: 存放在對象中用static定義的靜態成員(全局變量和靜態變量)和常量

參考文章:

Java內存空間詳解

JAVA的內存結構

Java內存空間分類

JVM的內存區域劃分(jdk7和jdk8)

Java虛擬機的內存組成以及堆內存介紹

相關文章
相關標籤/搜索