JVM之JVM的體系結構

1、JDK的組成

JDK:JDK是Java開發工具包,是Sun Microsystems針對Java開發員的產品。JDK中包含JRE(在JDK的安裝目錄下有一個名爲jre的目錄,裏面有兩個文件夾bin和lib,在這裏能夠認爲bin裏的就是jvm,lib中則是jvm工做所須要的類庫,而jvm和 lib和起來就稱爲jre)和一堆Java工具(javac/java/jdb等)和Java基礎的類庫(即Java API 包括rt.jar)。html

Java Runtime Environment(JRE):是運行基於Java語言編寫的程序所不可缺乏的運行環境。也是經過它,Java的開發者才得以將本身開發的程序發佈到用戶手中,讓用戶使用。JRE中包含了Java virtual machine(JVM),runtime class libraries和Java application launcher,這些是運行Java程序的必要組件。java

JVM(java virtual machine):就是咱們常說的java虛擬機,它是整個java實現跨平臺的最核心的部分,全部的java程序會首先被編譯爲.class的類文件,這種類文件能夠在虛擬機上執行。數據庫

2、JVM的位置

JVM就是運行在操做系統之上的一個軟件數組

 3、JVM體系結構

JVM的組成:安全

  • 類加載子系統 Class loader
  • 運行時數據區 JVM 內存模型
  • 執行引擎

4、類加載子系統


 ======================類加載器=======================網絡

 類加載器(ClassLoader):負責加載class文件(classs文件在文件開頭有特定的文件標識),將class文件字節碼內容加載到內存中,並將這些內容轉換成方法區中的運行時數據結構;ClassLoader只負責加載class文件的加載,至於它是否能夠運行,則由Execution Engine決定。數據結構

 

一、BootStrapLoader(引導類加載器):類加載器也是java類,他們也須要類加載器加載進入內存,顯然必需要有第一個不是java類的類加載器,來完成這個工做,這個正是BootStrap。負責加載存放在D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib下,或被-Xbootclasspath參數指定的路徑中的,而且能被虛擬機識別的類庫(如rt.jar,全部的java.*開頭的類均被Bootstrap ClassLoader加載);啓動類加載器是沒法被Java程序直接引用的;rt.jar 裏面的類的加載器都是BootStrapLoader。多線程

 二、Extension ClassLoader(擴展類加載器):該加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載D:\Program Files (x86)\Java\jdk1.7.0_79\jre\lib\ext目錄中,或者由java.ext.dirs系統變量指定的路徑中的全部類庫(如javax.*開頭的類),開發者能夠直接使用擴展類加載器。ext 目錄下全部的類的加載器都是Extension ClassLoaderapp

 

 三、Application ClassLoader(應用程序類加載器):該類加載器由sun.misc.Launcher$AppClassLoader來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者能夠直接使用該類加載器,若是應用程序中沒有自定義過本身的類加載器,通常狀況下這個就是程序中默認的類加載器。jvm

====================JVM類加載機制==============

全盤負責:當前線程的類加載器負責加載某個Class時,該Class所依賴的和引用的其餘Class也將由該類加載器負責載入,除非顯示使用CLassLoader.loadClass()指定類加載器來載入

父類委託:先讓父類加載器試圖加載該類,只有在父類加載器沒法加載該類時才嘗試從本身的類路徑中加載該類。因此咱們在開發中儘可能不要使用與JDK相同的類(例如自定義一個java.lang.System類),由於父類加載器中已經有一份java.lang.System類了,它會直接將該類給程序使用,而你自定義的類壓根就不會被加載。

雙親委派模型:

  雙親委派模型的工做流程是:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把請求委託給父加載器去完成,依次向上,所以,全部的類加載請求最終都應該被傳遞到頂層的啓動類加載器中,
只有當父加載器在它的搜索範圍中沒有找到所需的類時,即沒法完成該加載,子加載器纔會嘗試本身去加載該類。
雙親委派機制:

  • 一、當AppClassLoader加載一個class時,它首先不會本身去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成。
  • 二、當ExtClassLoader加載一個class時,它首先也不會本身去嘗試加載這個類,而是把類加載請求委派給BootStrap ClassLoader去完成。
  • 三、若是BootStrap ClassLoader加載失敗(例如在$JAVA_HOME/jre/lib裏未查找到該class),會使用ExtClassLoader來嘗試加載;
  • 四、若ExtClassLoader也加載失敗,則會使用AppClassLoader來加載,若是AppClassLoader也加載失敗,則會報出異常ClassNotFoundException。

雙親委派模型意義:

  •   -系統類防止內存中出現多份一樣的字節碼
  •   -保證Java程序安全穩定運行

 

 ==================類的加載過程======================

類的加載過程:JVM將javac編譯好的class字節碼文件加載到內存中,並對該數據進行驗證、解析和初始化、造成JVM能夠直接使用的JAVA類,最終回收(卸載)的過程。

字節碼(.class)文件來源:

  • – 從本地系統中直接加載
  • – 經過網絡下載.class文件
  • – 從zip,jar等歸檔文件中加載.class文件
  • – 從專有數據庫中提取.class文件
  • – 將Java源文件動態編譯爲.class文件

一、加載:加載階段其實就是JVM經過一個類的全限定名來獲取其定義的二進制字節流,並將這個字節流所表明的靜態存儲結構轉化爲方法區的運行時數據結構且在Java堆中生成一個表明這個類的java.lang.Class對象,做爲對方法區中這些數據的訪問入口。在該階段咱們開發人員能夠干預,例如:咱們能夠指定類加載器來加載該字節數組或者自定義類加載器來加載。

二、連接:將java類的二進制代碼合併到JVM的運行狀態中的過程

  • a、驗證:驗證是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。
  • b、準備:該階段是在方法區中爲類變量(static變量)分配內存並設置類變量初始值。例如:public static int flag=1;該階段初始化值爲0。
  • c、解析:虛擬機將常量池中的符號引用替換爲直接引用的過程。(直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄)

三、初始化:初始化爲類的靜態變量賦予正確的初始值,JVM負責對類進行初始化,主要對類變量進行初始化。

  • 初始化階段就是執行類構造器<clinit>()的過程,類構造器<clinit>()是由編譯器自動收集類中的全部類變量的賦值動做和靜態語句塊中的語句合併產生的。
  • 當初始化一個類的時候,若是發現其父類尚未進行過初始化,則須要先初始化其父類。
  • 虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步。
  • 當訪問一個java 類的靜態域時,只有正真申明這個域的類纔會被初始化。

四、使用:程序使用JVM加載的類

五、卸載 

  • 執行了System.exit()方法
  • JVM垃圾回收機制觸發回收
  • 程序正常執行結束
  • 程序在執行過程當中遇到了異常或錯誤而異常終止
  • 因爲操做系統出現錯誤而致使Java虛擬機進程終止

5、運行時數據區

一、方法區(Method Area):方法區是各個線程共享的內存區域;方法區用於存儲已被虛擬機加載的類的模板信息、常量、靜態變量等;雖然Java虛擬機規範把方法區描述爲堆的一部分,可是他還有個別名叫作Non-heap(非堆),目的應該是與Java堆區分開來;根據Java虛擬機規範的規定,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError 異常;相對而言,垃圾收集在這個區域是比較少出現的,但並不是數據進入了方法區就如永久代的名字同樣永久存在了。這區域的內存回收目標重要是針對常量池的回收和類型的卸載。
方法區只是一個規範:

  • 在HotSpot虛擬機上開發、部署程序咱們把方法區稱爲「永久代」(Permanent Generation);
  • 他虛擬機(如 BEA JRockit、IBM J9 等)來講是不存在永久代的概念的。
  • HotSpot虛擬機在JKD.8中已經沒有方法區的概念了,他使用元空間代替該區域

二、PC寄存器(程序計數器):每一個線程都有一個程序計數器,是線程私有的;就是一個指針,指向方法區中的方法字節碼(用來存儲指向下一條指令的地址,既將要執行的指令代碼),由執行引擎讀取下一條指令,是一個很是小的內存空間,幾乎能夠忽略不記;它是當前線程所執行的字節碼的行號指示器,字節碼解釋器經過改變這個計數器的值來選取下一條須要執行的字節碼指令。若是執行的是一個Native方法,那這個計數器是空的;用以完成分支、循環、跳轉、異常處理、線程恢復等基礎功能。不會發生內存溢出OOM錯誤
本地方法棧(Native Stack):與虛擬機棧基本相似,區別在於虛擬機棧爲虛擬機執行的java方法服務,而本地方法棧則是爲Native方法服務。(棧的空間大小遠遠小於堆)

三、虛擬機棧(Vm Stack)
  棧也叫棧內存,主管 Java 程序的運行,是在線程建立時建立,它的生命期是跟隨線程的生命期,線程結束棧內存也就仔放,對於棧來講不存在垃圾回收問題,只要線程結束該棧就釋放,生命週期和線程一致,是線程私有的。8種基木類型的變量+對象的引用變量+實例方法都是在函數的棧內存中分配。

棧的運行原理:棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法( Method )和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀 Fl ,並被壓入到棧中, A方法又調用了B方法,因而產生棧幀 F2 也被壓入棧,B方法又調用了C方法,因而產生棧幀 F3 也被壓入棧,執行完畢後,先彈出 F3 棧幀,再彈出 F2 棧幀,再彈出 Fl 棧幀 以此類推, 遵循「先進後出」 / 「後進先出」原則。每一個方法執行的同時都會建立一個棧幀,用於存儲局部變量表、操做數、動態連接、方法出口等信息,每個方法從調用直至執行完畢的過,就對應着一個棧幀在虛擬機中入棧到出棧的過程。棧的大小和具體JVM的實現有關,一般在 256K~1024K 之間, 1M 左右。

JVM棧的特色:

  • 局部變量表所需的內存空間在編譯期間完成內存分配。當進入一個方法時,這個方法須要在幀中分配多大的內存空間是徹底肯定的,在方法運行期間不會改變局部變量表的大小。
  • 在Java虛擬機規範中,對這個區域規定了兩種異常狀態:若是線程請求的棧的深度大於虛擬機容許的深度,將拋出StackOverFlowError異常(棧溢出);若是虛擬機棧能夠動態擴展(如今大部分Java虛擬機均可以動態擴展,只不過Java虛擬機規範中也容許固定長度的java虛擬機棧),若是擴展時沒法申請到足夠的內存空間,就會拋出OutOfMemoryError異常(沒有足夠的內存)。

四、本地方法棧(Native Method Stacks):與虛擬機棧所發揮的做用是很是類似的,他們之間的區別不過是虛擬機棧爲虛擬機執行Java方法(也就是字節碼)服務,而本地方法棧則爲虛擬機使用到的本地Native方法服務‘;在虛擬機規範中對本地方法棧中的使用方法、語言、數據結構並無強制規定,所以具體的虛擬機能夠自由實現它。甚至有的虛擬機(例如Sun HotSpot虛擬機)直接就把本地方法棧和虛擬機棧合二爲一。本地方法棧也會拋出StackOverFlowError和OutOfmMemoryError異常。


五、Java堆(Java Heap):是Java虛擬機管理內存中的最大一塊;Java堆是全部線程共享的一塊內存管理區域。此內存區域惟一目的就是存放對象的實例,幾乎全部對象實例都在堆中分配內存。這一點在Java虛擬機規範中的描述是:全部對象實例以及數組都要在堆上分配,可是隨着JIT編譯器的發展與逃逸技術逐漸成熟,棧上分配、標量替換優化技術將會致使一些微妙的變化發生,全部的對象都分配在堆上也不是變的那麼「絕對」了。詳解請學習個人:JVM之堆的體系結構

相關文章
相關標籤/搜索