JVM(Java虛擬機)是一個抽象的計算模型。就如同一臺真實的機器,它有本身的指令集和執行引擎,能夠在運行時操控內存區域。目的是爲構建在其上運行的應用程序提供一個運行環境。JVM能夠解讀指令代碼並與底層進行交互:包括操做系統平臺和執行指令並管理資源的硬件體系結構。本文主要對JVM進行概述,並介紹Java程序是如何在上面執行的。html
從本質上講,虛擬機是個被構建來提供特定或通用目的服務環境的非實體計算機。這聽起來像是一個仿真器,用來仿真機器未配置或不能按要求執行任務的硬件組件。所以,咱們要作的就是建立一個軟件,以軟件的形式模擬硬件提供的服務,使之看起來這個特定的硬件在系統中是實際存在的。虛擬機在必定程度上使用CPU虛擬化,爲實際的硬件問題提供一個接口。因此能夠說它實際上有兩種功能:提供一個虛擬的環境,或者將某些不存在的事物進行抽象化。可是當咱們深刻了解以後會發現,這兩種功能有着很明顯的不一樣。咱們如今暫且不看它們之間的不一樣點,它們的共同點在於都「僞裝」成它們不是的東西。正如Popek和Goldberg在論文「Formal Requirements for Virtualizable Third Generation Architectures」裏說的,它是「一個真實機器有效、獨立的複製品。」java
基於不一樣的需求和用途,虛擬機有不少類型。一種叫徹底虛擬化(full virtualization),這種虛擬機表現得像一臺真正的機器。其餘類型的虛擬機會更精細,更專業,好比進程虛擬化(process virtualization)。而對JVM進行分類是很困難的,由於它對CPU進行了虛擬化,有本身的運行時環境、與底層平臺協調工做的內存管理器、垃圾收集器,固然還有大量做爲中間字節碼輸入的類庫,最後但一樣重要的是,它可以模擬機器的寄存器、堆棧等等。簡單地說,它是被Java編譯器編譯爲java的本質——字節碼的遊樂場。字節碼其實是JVM用來將代碼從新翻譯爲本地機器指令所使用的機器代碼。編程
有趣的是,其實JVM並不關心Java語言或其餘編程語言的語義和語法結構。當JVM執行一段程序的時候,它主要關注的是一種稱爲「類文件」的特定文件格式。*.class類文件格式和Java代碼定義的面向對象的類結構毫無關係。編譯器將*.java文件編譯成*.class文件,而後JVM對*.class文件進行解譯,它不關心這個類文件是由哪一種編譯器生成的,只要符合類文件的文件格式便可。Java編譯器將一段程序編譯爲等價的類文件。這些類文件實際上包含了半編譯的代碼——字節碼。之因此稱之爲半編譯,是由於字節碼並不像C/C++編譯器編譯的二進制文件同樣會被直接執行。字節碼要先被輸入到JVM中,而後再轉換爲底層平臺能夠執行的最終指令。因此字節碼包含了JVM的指令、符號表和其餘的輔助信息。無論何種語言,能根據JVM的語法和結構約束編譯生成字節碼的編譯器,都是一個能夠在JVM上執行的候選者。bootstrap
JVM將自身定位於字節碼和底層平臺之間。底層平臺是指操做系統(OS)和硬件。操做系統和硬件體系結構在不一樣的機器上可能不一樣,可是同一段Java程序能夠不用作任何的代碼修改就能在不一樣的機器上運行。這是在虛擬環境中執行的程序語言的獨特之處。例如,由其餘程序語言編譯器編譯的目標代碼如C++和Java相比的不一樣點在於,C++程序須要被特定平臺的編譯器從新編譯,從而使它能在不一樣的體系結構上面運行。而Java代碼並不須要作任何改變,由於由Java編譯器編譯的字節碼是在外圍的JVM上執行。所以,JVM負責從新解譯由Java編譯器生成的字節碼,並和底層平臺協調工做。也就是說,儘管Java編譯器生成的結果是平臺獨立的,但JVM與特定平臺相關的。除非兩臺機器有相同的體系結構,在某個體系結構上安裝和使用的JVM可能換一臺機器就不能正常工做了。數組
想要運行Java程序,咱們須要JVM由於它提供了字節碼的運行環境。Oracle提供了兩種不一樣的產品:JDK(Java開發工具)和JRE(Java運行環境)。JRE是咱們安裝運行Java程序的最基本軟件。它和Java類庫以及運行Java程序所須要的其餘組件一塊兒夠成了JVM的一個實現。因此,若是咱們想運行一個類文件或一段字節碼,僅須要JRE就夠了。而JDK(Java開發工具)是JRE的超集。它包含了JRE提供的全部東西,包括建立類文件的工具如Java編譯器、調試器和其餘許多開發Java程序相關的工具。因此,當咱們要建立類文件(編譯Java源碼)時,咱們就須要JDK。下面是一張Java API文檔的截圖。注意組成JDK,JRE和Java SE API核心類庫的組件;經過這張截圖你能夠了解JRE和JDK裏面都有哪些內容。jvm
Java提供了Java虛擬機規範來讓咱們對JVM的工做原理有一個完整的認識。你能夠從這裏獲得概念性知識,並開發一個本身的JVM;但這並非一個簡單的工做。如今市場上已經有不少JVM了,其中有些是免費的,還有一些須要購買商業許可證才能使用。編程語言
每個在JRE上運行的Java程序都會建立一個JVM實例。編譯後的Java類文件和其餘被依賴的類文件會被加載到運行環境中。這一步由類加載器協助完成。工具
類加載器經過三步完成類加載。性能
Firstly, it loads the program classes, along with standard Java classes that are bundled with JDK in the form of bytecode. The standard classes form the core API library of Java. The bootstrap begins by locating the core API libraries classes typically situated in jre/lib.開發工具
首先,類加載器會以字節碼的形式加載程序類文件和與JDK綁定的標準Java類文件。標準類文件構成了Java API核心類庫。引導程序經過定位一般位於jre/lib目錄下的核心API類庫啓動。
而後,擴展機制定位擴展類庫,例如一些爲開發或執行代碼而被添加到Java裏新的(可選)包。擴展類一般位於 jre/lib/ext目錄下。有時,擴展類會被放到系統屬性java.ext.dirs 定義的其餘目錄下面。程序包使用JAR或ZIP的擴展名。
最後,若是要加載的類沒有在Java的標準類庫或擴展類庫中被找到,加載器會搜索CLASSPATH環境變量下定義的文件路徑,CLASSPATH裏面包含了諸多存儲類文件的地址。系統屬性java.class.path對CLASSPATH環境變量作了映射。
像JAR或ZIP這樣的歸檔文件都是包含了一些其餘文件目錄的獨立文件,一般是壓縮文件格式。例如,程序中使用的標準類庫包含在歸檔文件 rt.jar中,該文件會和JDK被一同安裝。
一旦文件被定位並加載以後,類加載器會執行不一樣的功能,例如根據JVM的約束進行校驗、內存分配,或者在調用構造器設置定義的變量元素以前使用默認值初始化類變量。
當加載程序結束以後,字節碼指令被傳遞給執行引擎。而後JVM藉助於綁定到指定平臺的特定JVM實現的本地代碼和底層操做系統進行交互。請注意,不一樣平臺的實現可能有略微不一樣。
數據存儲區的堆空間用於存儲動態或臨時分配的內存空間。類和數組是在這塊區域裏建立的。當建立對象大小超出堆內存空間時,垃圾收集器會回收內存。
Java棧,又叫棧幀,用於存儲局部變量和不一樣階段方法調用的臨時結果。每一次方法調用都會建立一個棧幀。
方法區基本上是JVM線程間的共享存儲區。
寄存器是一個模擬的底層機器寄存器,主要用於執行字節碼指令。PC寄存器或程序計數器是用於保存當前指令執行地址的主要寄存器。
JVM的功能能夠概括爲:
使用虛擬機執行程序的最大好處是它是平臺獨立的。和C/C++這種高效的語言相比,這種類型編程語言的生產力能夠彌補其性能上的弱點。本文僅僅是對JVM的一點淺見,但也許已經足以幫助理解JVM是如何實際工做的。
原文連接: developer 翻譯: ImportNew.com - 辰午
譯文連接: http://www.importnew.com/29224.html