JVM入門——JVM內存結構

1、java代碼編譯執行過程

  1.源碼編譯:經過Java源碼編譯器將Java代碼編譯成JVM字節碼(.class文件)html

  2.類加載:經過ClassLoader及其子類來完成JVM的類加載java

  3.類執行:字節碼被裝入內存,進入JVM虛擬機,被解釋器解釋執行編程

   注:Java平臺由Java虛擬機和Java應用程序接口搭建,Java語言則是進入這個平臺的通道,小程序

      用Java語言編寫並編譯的程序能夠運行在這個平臺上數組

2、JVM簡介

1.java程序通過一次編譯以後,將java代碼編譯爲字節碼也就是class文件,而後在不一樣的操做系統上依靠不一樣的java虛擬機進行解釋,最後再轉換爲不一樣平臺的機器碼,最終獲得執行多線程

2.Java虛擬機(JVM) 處在覈心的位置,是程序與底層操做系統、硬件無關的關鍵。oracle

    JVM的下方是移植接口,移植接口由兩部分組成:適配器和Java操做系統, 其中依賴於平臺的部分稱爲適配器,JVM 經過移植接口在具體的平臺和操做系統上實現app

    JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application) 和小程序(Java applet) 能夠在任何Java平臺上運行而無需考慮底層平臺編程語言

    Java虛擬機(JVM)實現了程序與操做系統的分離,從而實現了Java 的跨平臺函數

        

3.JVM在它的生存週期中有一個明確的任務,那就是運行Java程序,所以當Java程序啓動的時候,就產生JVM的一個實例;當程序運行結束的時候,該實例也跟着消失了

4.三種JVM:① Sun公司的HotSpot  ② BEA公司的JRockit  ③ IBM公司的J9 JVM

    在JDK1.7及其之前咱們所使用的都是Sun公司的HotSpot,但因爲Sun公司和BEA公司都被oracle收購,jdk1.8將採用Sun公司的HotSpot和BEA公司的JRockit兩個JVM中精華造成jdk1.8的JVM。

3、JVM體系結構

  

  

1.Class Loader類加載器
       負責加載 .class文件,class文件在文件開頭有特定的文件標示,而且ClassLoader負責class文件的加載等,至於它是否能夠運行,則由Execution Engine決定。
  ① 定位和導入二進制class文件
  ② 驗證導入類的正確性
  ③ 爲類分配初始化內存
  ④ 幫助解析符號引用.
2.Native Interface本地接口
  本地接口的做用是融合不一樣的編程語言爲Java所用,它的初衷是融合C/C++程序,Java誕生的時候C/C++橫行的時候,要想立足,必須有調用C/C++程序,因而就在內存中專門開闢了一塊區域處理標記爲
  native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies。
  目前該方法使用的愈來愈少了,除非是與硬件有關的應用,好比經過Java程序驅動打印機,或者Java系統管理生產設備,在企業級應用中已經比較少見。
  由於如今的異構領域間的通訊很發達,好比可使用Socket通訊,也可使用Web Service等。
3.Execution Engine 執行引擎:執行包在裝載類的方法中的指令,也就是方法。
4.Runtime data area 運行數據區(即:虛擬機內存或者JVM內存 下節介紹)
      從整個計算機內存中開闢一塊內存存儲Jvm須要用到的對象,變量等,分爲:方法區,堆,虛擬機棧,程序計數器,本地方法棧。

4、JVM內存結構

 

1.程序計數器 PC Register

  每一個線程都有一個程序計算器,就是一個指針,指向方法區中的方法字節碼(下一個將要執行的指令代碼),由執行引擎讀取下一條指令,是一個很是小的內存空間,幾乎能夠忽略不記。

  程序計數器(Program Counter Register)是一塊較小的內存空間,它的做用能夠看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏(僅是概念模型,各類虛擬機可能會經過一些更高效的方式去實現),字節碼解釋器工做時就是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。因爲Java 虛擬機的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個肯定的時刻,一個處理器(對於多核處理器來講是一個內核)只會執行一條線程中的指令。所以,爲了線程切換後能恢復到正確的執行位置,每條線程都須要有一個獨立的程序計數器,各條線程之間的計數器互不影響,獨立存儲,咱們稱這類內存區域爲「線程私有」的內存。若是線程正在執行的是一個Java 方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;若是正在執行的是Natvie 方法,這個計數器值則爲空(Undefined)。此內存區域是惟一一個在Java 虛擬機規範中沒有規定任何OutOfMemoryError 狀況的區域。

2.本地方法棧 Native Method Stack

  Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies

  本地方法棧與虛擬機棧基本相似,區別在於虛擬機棧爲虛擬機執行的java方法服務,而本地方法棧則是爲Native方法服務

3.方法區  Method Area

  用於存儲虛擬機加載的:靜態變量+常量+類信息+運行時常量池 (類信息:類的版本、字段、方法、接口、構造函數等描述信息 )

  默認最小值爲16MB,最大值爲64MB,能夠經過-XX:PermSize 和 -XX:MaxPermSize 參數限制方法區的大小

  對於習慣在HotSpot 虛擬機上開發和部署程序的開發者來講,不少人願意把方法區稱爲「永久代」(Permanent Generation),本質上二者並不等價,僅僅是由於HotSpot 虛擬機的設計團隊選擇把GC 分代收集擴展至方法區,或者說使用永久代來實現方法區而已。對於其餘虛擬機(如BEA JRockit、IBM J9 等)來講是不存在永久代的概念的。即便是HotSpot 虛擬機自己,根據官方發佈的路線圖信息,如今也有放棄永久代並「搬家」至Native Memory 來實現方法區的規劃了。Java 虛擬機規範對這個區域的限制很是寬鬆,除了和Java 堆同樣不須要連續的內存和能夠選擇固定大小或者可擴展外,還能夠選擇不實現垃圾收集。相對而言,垃圾收集行爲在這個區域是比較少出現的,但並不是數據進入了方法區就如永久代的名字同樣「永久」存在了。這個區域的內存回收目標主要是針對常量池的回收和對類型的卸載,通常來講這個區域的回收「成績」比較難以使人滿意,尤爲是類型的卸載,條件至關苛刻,可是這部分區域的回收確實是有必要的。在Sun 公司的BUG 列表中,曾出現過的若干個嚴重的BUG 就是因爲低版本的HotSpot 虛擬機對此區域未徹底回收而致使內存泄漏。根據Java 虛擬機規範的規定,當方法區沒法知足內存分配需求時,將拋出OutOfMemoryError 異常。

4.棧 JVM Stack

  編譯器可知的各類基本數據類型(booleanbytecharshortintfloatlongdouble)、對象引用(引用指針,並不是對象自己)

  棧是java 方法執行的內存模型:

  每一個方法被執行的時候 都會建立一個「棧幀」用於存儲局部變量表(包括參數)、操做棧、方法出口等信息。

  每一個方法被調用到執行完的過程,就對應着一個棧幀在虛擬機棧中從入棧到出棧的過程。

  (局部變量表:存放了編譯器可知的各類基本數據類型(booleanbytecharshortintfloatlongdouble)、對象引用(引用指針,並不是對象自己),

  其中64位長度的long和double類型的數據會佔用2個局部變量的空間,其他數據類型只佔1個。

  局部變量表所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法須要在棧幀中分配多大的局部變量是徹底肯定的,在運行期間棧幀不會改變局部變量表的大小空間)

  棧的生命期是跟隨線程的生命期,線程建立時建立,線程結束棧內存也就釋放,是線程私有的。

5.堆  Java Heap

  全部的對象實例以及數組都要在堆上分配,此內存區域的惟一目的就是存放對象實例

  堆是Java 虛擬機所管理的內存中最大的一塊。Java 堆是被全部線程共享的一塊內存區域,在虛擬機啓動時建立

  堆是理解Java GC機制最重要的區域,沒有之一

  結構:新生代(Eden區+2個Survivor區)  老年代   永久代(HotSpot有)

  新生代:新建立的對象——>Eden區 

  GC以後,存活的對象由Eden區 Survivor區0進入Survivor區1   

  再次GC,存活的對象由Eden區 Survivor區1進入Survivor區0 

  老年代:對象若是在新生代存活了足夠長的時間而沒有被清理掉(即在幾回Young GC後存活了下來),則會被複制到老年代

  若是新建立對象比較大(好比長字符串或大數組),新生代空間不足,則大對象會直接分配到老年代上(大對象可能觸發提早GC,應少用,更應避免使用短命的大對象)

  老年代的空間通常比新生代大,能存放更多的對象,在老年代上發生的GC次數也比年輕代少

  永久代:能夠簡單理解爲方法區(本質上二者並不等價)

  如上文所說:對於習慣在HotSpot 虛擬機上開發和部署程序的開發者來講,不少人願意把方法區稱爲「永久代」,本質上二者並不等價

  僅僅是由於HotSpot 虛擬機的設計團隊選擇把GC 分代收集擴展至方法區,或者說使用永久代來實現方法區而已

  對於其餘虛擬機(如BEA JRockit、IBM J9 等)來講是不存在永久代的概念的

  即便是HotSpot 虛擬機自己,根據官方發佈的路線圖信息,如今也有放棄永久代並「搬家」至Native Memory 來實現方法區的規劃了

  Jdk1.6及以前:常量池分配在永久代

  Jdk1.7:有,但已經逐步「去永久代」

  Jdk1.8及以後:沒有永久代(java.lang.OutOfMemoryError: PermGen space,這種錯誤將不會出如今JDK1.8中)

6.直接內存  Direct Memor

  直接內存並非JVM管理的內存,能夠這樣理解,直接內存,就是JVM之外的機器內存,好比,你有4G的內存,JVM佔用了1G,則其他的3G就是直接內存

  JDK中有一種基於通道(Channel)和緩衝區(Buffer)的內存分配方式,將由C語言實現的native函數庫分配在直接內存中,用存儲在JVM堆中的DirectByteBuffer來引用

  因爲直接內存收到本機器內存的限制,因此也可能出現OutOfMemoryError的異常。

    

 

參考資料

1.JVM工做原理 

2.JVM運行原理詳解

3.Java 內存區域和GC機制

4.深刻理解JVM基本原理

相關文章
相關標籤/搜索