經典面試題|講一講JVM的組成

JVM(Java 虛擬機)算是面試必問的問題的了,而但凡問 JVM 必定會問的第一個問題就是:講一講 JVM 的組成?那本文就注重講一下 JVM 的組成。java

首先來講 JVM 的組成分爲,總體組成部分和運行時數據區組成部分,通常開發者關注的和麪試官問的都是後者,但本文會詳細講解以上兩個組成部分。程序員

1、JVM 總體組成

JVM 總體組成可分爲如下四個部分:

  1. 類加載器(ClassLoader)面試

  2. 運行時數據區(Runtime Data Area)多線程

  3. 執行引擎(Execution Engine)jvm

  4. 本地庫接口(Native Interface)優化

各個組成部分的用途:

程序在執行以前先要把java代碼轉換成字節碼(class文件),jvm首先須要把字節碼經過必定的方式 類加載器(ClassLoader) 把文件加載到內存中 運行時數據區(Runtime Data Area) ,而字節碼文件是jvm的一套指令集規範,並不能直接交個底層操做系統去執行,所以須要特定的命令解析器 執行引擎(Execution Engine) 將字節碼翻譯成底層系統指令再交由CPU去執行,而這個過程當中須要調用其餘語言的接口 本地庫接口(Native Interface)來實現整個程序的功能,這就是這4個主要組成部分的職責與功能。spa

而咱們一般所說的jvm組成指的是運行時數據區(Runtime Data Area),由於一般須要程序員調試分析的區域就是「運行時數據區」,或者更具體的來講就是「運行時數據區」裏面的Heap(堆)模塊,那接下來咱們來看運行時數據區(Runtime Data Area)是由哪些模塊組成的。操作系統

2、運行時數據區組成

jvm的運行時數據區,不一樣虛擬機實現可能略微有所不一樣,但都會聽從Java虛擬機規範,Java 8 虛擬機規範規定,Java虛擬機所管理的內存將會包括如下幾個運行時數據區域:線程

  1. 程序計數器(Program Counter Register)翻譯

  2. Java虛擬機棧(Java Virtual Machine Stacks)

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

  4. Java堆(Java Heap)

  5. 方法區(Methed Area)

接下來咱們分別介紹每一個區域的用途。

①、Java程序計數器

程序計數器(Program Counter Register)是一塊較小的內存空間,它能夠看做是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型裏,字節碼解析器的工做是經過改變這個計數器的值來選取下一條須要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都須要依賴這個計數器來完成。

特性:內存私有

因爲jvm的多線程是經過線程輪流切換並分配處理器執行時間的方式來實現的,也就是任什麼時候刻,一個處理器(或者說一個內核)都只會執行一條線程中的指令。所以爲了線程切換後能恢復到正確的執行位置,每一個線程都有獨立的程序計數器。

異常規定:無

若是線程正在執行Java中的方法,程序計數器記錄的就是正在執行虛擬機字節碼指令的地址,若是是Native方法,這個計數器就爲空(undefined),所以該內存區域是惟一一個在Java虛擬機規範中沒有規定OutOfMemoryError的區域。

②、Java虛擬機棧

Java虛擬機棧(Java Virtual Machine Stacks)描述的是Java方法執行的內存模型,每一個方法在執行的同時都會建立一個線幀(Stack Frame)用於存儲局部變量表、操做數棧、動態連接、方法出口等信息,每一個方法從調用直至執行完成的過程,都對應着一個線幀在虛擬機棧中入棧到出棧的過程。

特性:內存私有,它的生命週期和線程相同。

異常規定:StackOverflowError、OutOfMemoryError

一、若是線程請求的棧深度大於虛擬機所容許的棧深度就會拋出StackOverflowError異常。

二、若是虛擬機是能夠動態擴展的,若是擴展時沒法申請到足夠的內存就會拋出OutOfMemoryError異常。

③、本地方法棧

本地方法棧(Native Method Stack)與虛擬機棧的做用是同樣的,只不過虛擬機棧是服務Java方法的,而本地方法棧是爲虛擬機調用Native方法服務的。

在Java虛擬機規範中對於本地方法棧沒有特殊的要求,虛擬機能夠自由的實現它,所以在Sun HotSpot虛擬機直接把本地方法棧和虛擬機棧合二爲一了。

特性和異常: 同虛擬機棧,請參考上面知識點。

④、Java堆

Java堆(Java Heap)是Java虛擬機中內存最大的一塊,是被全部線程共享的,在虛擬機啓動時候建立,Java堆惟一的目的就是存放對象實例,幾乎全部的對象實例都在這裏分配內存,隨着JIT編譯器的發展和逃逸分析技術的逐漸成熟,棧上分配、標量替換優化的技術將會致使一些微妙的變化,全部的對象都分配在堆上漸漸變得不那麼「絕對」了。

特性:內存共享

異常規定:OutOfMemoryError

若是在堆中沒有內存完成實例分配,而且堆不能夠再擴展時,將會拋出OutOfMemoryError。

Java虛擬機規範規定,Java堆能夠處在物理上不連續的內存空間中,只要邏輯上連續便可,就像咱們的磁盤空間同樣。在實現上也能夠是固定大小的,也能夠是可擴展的,不過當前主流的虛擬機都是可擴展的,經過-Xmx和-Xms控制。

⑤、方法區

方法區(Methed Area)用於存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯後的代碼等數據。

誤區:方法區不等於永生代

不少人緣由把方法區稱做「永久代」(Permanent Generation),本質上二者並不等價,只是HotSpot虛擬機垃圾回收器團隊把GC分代收集擴展到了方法區,或者說是用來永久代來實現方法區而已,這樣能省去專門爲方法區編寫內存管理的代碼,可是在Jdk8也移除了「永久代」,使用Native Memory來實現方法區。

特性:內存共享

異常規定:OutOfMemoryError

當方法沒法知足內存分配需求時會拋出OutOfMemoryError異常。

3、擴展知識

本節將擴展一些和內存分配有關的知識。

運行時常量池

運行時常量池是方法區的一部分,Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池(Constant Pool Table)用於存放編譯期生成的各類字面量和符號引用,這部分在類加載後進入方法區的運行是常量池中,如String類的intern()方法。

直接內存

直接內存(Direct Memory)並非虛擬機運行時數據區的一部分,但這部份內存也會被頻繁的使用,並且可能致使OutOfMemoryError。在JDK 1.4中新加入了NIO類,引入了一種基於Channel與緩衝區Buffer的IO方式,它經過一個存儲在Java堆中的DirectByteBuffer對象做爲這塊內存的引用操做,它所以更高效,它避免了Java堆和Native堆來回交換數據的時間。

注意 :直接內存分配不會受到Java堆大小的限制,可是受到本機總內存大小限制,在設置虛擬機參數的時候,不能忽略直接內存,把實際內存設置爲-Xmx,使得內存區域的總和大於物理內存的限制,從而致使動態擴展時出現OutOfMemoryError異常。

4、總結

本文講了jvm的主要組成部分,以及組成部分中最重要的運行時數據區(Runtime Data Area)的構成,其中程序計數器、虛擬機棧和本地方法爲私有內存,會隨着線程而生,隨着線程而滅,而Java堆做爲最大的內存區域將是開發人員重點關注的內存區域,還有方法區以及運行時常量區與永生代的關係,最後講了直接內存的實現過程已經使用時須要主要的點,但願可以幫助你們更好的理解jvm。

5、參考資料

《深刻理解Java虛擬機》

【End】

相關文章
相關標籤/搜索