java虛擬機

java虛擬機

Java虛擬機是一種抽象化的計算機,通過在實際的計算機上仿真模擬各種計算機功能來實現的。Java虛擬機有自己完善的硬體架構,如處理器、堆疊、暫存器等,還具有相應的指令系統。JVM禁止了與具體作業系統平台相關的信息,使得Java程式只需生成在Java虛擬機上運行的目標代碼(位元組碼),就可以在多種平台上不加修改地運行。

基本信息

定義

Java虛擬機(JavaVirtualMachine簡稱JVM)是運行所有Java程式的抽象計算機,是Java語言的運行環境,它是Java最具吸引力的特性之一。對Java感興趣,想學習Java不知道怎么學,從事java工作的人,想認識學習java,可以加群,群號四一八三,五五五,加上三一,按順序組合,裡面有免費資料

簡介

Java虛擬機(JVM)一種用於計算機設備的規範,可用不同的方式(軟體或硬體)加以實現。編譯虛擬機的指令集與編譯微處理器的指令集非常類似。Java虛擬機包括一套位元組碼指令集、一組暫存器、一個棧、一個垃圾回收堆和一個存儲方法域。
Java虛擬機(JVM)是可運行Java代碼的假想計算機。只要根據JVM規格描述將解釋器移植到特定的計算機上,就能保證經過編譯的任何Java代碼能夠在該系統上運行。
Java虛擬機是一個想像中的機器,在實際的計算機上通過軟體模擬來實現。Java虛擬機有自己想像中的硬體,如處理器、堆疊、暫存器等,還具有相應的指令系統。
Java虛擬機規範定義了一個抽象的——而非實際的——機器或處理器。這個規範描述了一個指令集,一組暫存器,一個堆疊,一個“垃圾堆”,和一個方法區。一旦一個Java虛擬機在給定的平台上運行,任何Java程式(編譯之後的程式,稱作位元組碼)都能在這個平台上運行。Java虛擬機(JVM)可以以一次一條指令的方式來解釋位元組碼(把它映射到實際的處理器指令),或者位元組碼也可以由實際處理器中稱作just-in-time的編譯器進行進一步的編譯。

特點

Java語言的一個非常重要的特點就是與平台的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平台上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機後,Java語言在不同平台上運行時不需要重新編譯。Java語言使用模式Java虛擬機禁止了與具體平台相關的信息,使得Java語言編譯程式只需生成在Java虛擬機上運行的目標代碼(位元組碼),就可以在多種平台上不加修改地運行。Java虛擬機在執行位元組碼時,把位元組碼解釋成具體平台上的機器指令執行。

使用主體

Java虛擬機是Java語言底層實現的基礎。這有助於理解Java語言的一些性質,也有助於使用Java語言。對於要在特定平台上實現Java虛擬機的軟體人員,Java語言的編譯器作者以及要用硬體晶片實現Java虛擬機的人來說,則必須深刻理解Java虛擬機的規範。另外,如果你想擴展Java語言,或是把其它語言編譯成Java語言的位元組碼,你也需要深入地了解Java虛擬機。

安裝方法

java虛擬機安裝方法java虛擬機安裝方法

下載解壓:

下載j2sdk-1_4_2_05-linux-i586.bin隨便放到一個目錄里,比如/tmp。

在終端里輸入:sh j2sdk-1_4_2_05-linux-i586.bin回車

之後會出現一堆軟體說明,按回車n次直到問你yes or no,當然回答yes,輸入y,回車後開始解壓縮。

完成之後,在/tmp里就會出現一個名為j2sdk1.4.2_05的資料夾。

安裝:

安裝很簡單:將j2sdk1.4.2_05資料夾複製到/usr目錄里。

設定環境變數:

只有設定好環境變數,系統才能調用java虛擬環境

打開/etc/profile檔案,在相關位置中加入:

export JAVA_HOME=/usr/j2sdk1.4.2_05

export PATH=/usr/j2sdk1.4.2_05/bin:$PATH

export CLASSPATH=/usr/j2sdk1.4.2_05/lib:/usr/j2sdk1.4.2_05/jre/lib:.:

保存

設定中文字型:

注意:下面涉及到的檔案請先備份,以防萬一!

進入/usr/j2sdk1.4.2_05/jre/lib/資料夾

刪除裡面所有帶.zn的文檔,只留下font.properties.zh文檔。

安裝simsun字型如果不喜歡simsun可以不裝。

編輯font.properties.zh,將所有-tlc-song-medium-r-normal--*-%d-*-*-c-*-gbk-0 替換成:

-misc-simsun-medium-r-normal--*-%d-*-*-c-*-gbk-0(如果沒裝simsun字型,可以將-simsun-那裡改成你喜歡的字型,前提是該字型在系統中存在)

之後在終端中轉到目錄/usr/j2sdk1.4.2_05/jre/bin/下

輸入命令:

./ControlPanel回車

數據類型

Java 虛擬機支持Java語言的基本數據類型有8種,注意String不是基本數據類型如下:

boolean://1位元組有符號整數的補碼

byte://1位元組有符號整數的補碼

short://2 位元組有符號整數的補碼

int://4 位元組有符號整數的補碼

long://8位元組有符號整數的補碼

float://4位元組IEEE754 單精度浮點數

double://8位元組IEEE754 雙精度浮點數

char://2 位元組無符號Unicode 字元

幾乎所有的Java類型檢查都是在編譯時完成的。上面列出的原始 數據類型的數據在Java執行時不需要用硬體標記。操作這些原始 數據類型數據的 位元組碼(指令)本身就已經指出了 運算元的數據類型,例如iadd、ladd、fadd和dadd指令都是把兩個數相加,其運算元類型別是int、long、float和double。 虛擬機沒有給boolean(布爾)類型設定單獨的指令。boolean型的數據是由integer指令,包括integer返回來處理的。boolean型的 數組則是用byte數組來處理的。 虛擬機使用IEEE754格式的浮點數。不支持 IEEE格式的較舊的計算機,在運行Java數值計算程式時,可能會非常慢。

虛擬機支持的其它 數據類型包括:

object//對一個Javaobject(對象)的4位元組引用

returnAddress//4位元組,用於jsr/ret/jsr-w/ret-w指令

註:Java 數組被當作object處理。

虛擬機的規範對於object內部的結構沒有任何特殊的要求。在Sun公司的實現中,對object的引用是一個句柄,其中包含一對 指針:一個指針指向該object的方法表,另一個指向該object的數據。用Java虛擬機的 位元組碼錶示的程式應該遵守類型規定。Java虛擬機的實現應拒絕執行違反了類型規定的 位元組碼程式。Java虛擬機由於 位元組碼定義的限制似乎只能運行於32位 地址空間的機器上。但是可以創建一個Java虛擬機,它自動地把 位元組碼轉換成64位的形式。從Java虛擬機支持的 數據類型可以看出,Java對數據類型的內部格式進行了嚴格規定,這樣使得各種Java虛擬機的實現對數據的解釋是相同的,從而保證了Java的與平台無關性和 可移植性。

規格描述

JVM的設計目標是提供一個基於抽象規格描述的計算機模型,為解釋程式開發人員提范的任何系統上運行。JVM對其實現的某些方面給出了具體的定義,特別是對Java 可執行代碼,即 位元組碼(Bytecode)的格式給出了明確的規格。這一規格包括 操作碼和 運算元的語法和數值、 標識符的數值表示方式、以及Java類檔案中的Java對象、 常量緩衝池在JVM的存儲映象。這些定義為JVM 解釋器開發人員提供了所需的信息和開發環境。Java的設計者希望給開發人員以隨心所欲使用Java的自由。

JVM定義了控制Java代碼 解釋執行和具體實現的五種規格,它們是:

*JVM 指令系統

*JVM 暫存器

*JVM棧結構

*JVM碎片回收堆

*JVM存儲區

JVM指令系統

JVM指令系統同其他計算機的指令系統極其相似。Java指令也是由 操作碼和運算元兩部分組成。 操作碼為8位二進制數, 運算元進緊隨在操作碼的後面,其長度根據需要而不同。 操作碼用於指定一條指令操作的性質(在這裡我們採用彙編符號的形式進行說明),如iload表示從存儲器中裝入一個整數,anewarray表示為一個新 數組分配空間,iand表示兩個整數的"與",ret用於 流程控制,表示從對某一方法的調用中返回。當長度大於8位時, 運算元被分為兩個以上 位元組存放。JVM採用了"big endian"的編碼方式來處理這種情況,即高位bits存放在低 位元組中。這同 Motorola及其他的RISC CPU採用的 編碼方式是一致的,而與Intel採用的"little endian "的編碼方式即低位bits存放在低位 位元組的方法不同。Java指令系統是以Java語言的實現為目的設計的,其中包含了用於調用方法和監視多執行緒系統的指令。Java的8位 操作碼的長度使得JVM最多有256種指令,java1.6及以上版本已使用了160多種操作碼。

JVM暫存器

所有的CPU均包含用於保存系統狀態和處理器所需信息的 暫存器組。如果 虛擬機定義較多的 暫存器,便可以從中得到更多的信息而不必對棧或記憶體進行訪問,這有利於提高運行速度。然而,如果 虛擬機中的 暫存器比實際CPU的暫存器多,在實現虛擬機時就會占用處理器大量的時間來用常規 存儲器模擬暫存器,這反而會降低虛擬機的效率。針對這種情況,JVM只設定了4個最為常用的 暫存器。它們是:pc 程式計數器optop 運算元棧頂 指針frame當前執行環境指針 vars指向當前執行環境中第一個 局部變數的指針 所有 暫存器均為32位。pc用於記錄程式的執行。optop,frame和vars用於記錄指向Java棧區的 指針。

JVM棧結構

作為基於棧結構的計算機,Java棧是JVM存儲信息的主要方法。當JVM得到一個Java 位元組碼 應用程式後,便為該代碼中一個類的每一個方法創建一個棧框架,以保存該方法的狀態信息。每個棧框架包括以下三類信息:局部 變數執行環境 運算元棧 局部變數用於存儲一個類的方法中所用到的 局部變數。vars 暫存器指向該變數表中的第一個 局部變數。

執行環境用於保存 解釋器對Java 位元組碼進行解釋過程中所需的信息。它們是:上次調用的方法、局部變數 指針和 運算元棧的棧頂和棧底指針。執行環境是一個執行一個方法的控制中心。例如:如果 解釋器要執行iadd(整數加法),首先要從frame 暫存器中找到當前執行環境,而後便從執行環境中找到 運算元棧,從棧頂彈出兩個整數進行加法運算,最後將結果壓入棧頂。

運算元棧用於存儲運算所需運算元及運算的結果。

JVM碎片回收堆

Java類的實例所需的 存儲空間是在堆上分配的。 解釋器具體承擔為類實例分配空間的工作。 解釋器在為一個實例分配完 存儲空間後,便開始記錄對該實例所占用的記憶體區域的使用。一旦對象使用完畢,便將其回收到堆中。在Java語言中,除了new語句外沒有其他方法為一對象申請和釋放記憶體。對記憶體進行釋放和回收的工作是由Java運行系統承擔的。這允許Java運行系統的設計者自己決定碎片回收的方法。在SUN公司開發的Java 解釋器和Hot Java環境中,碎片回收用 後台執行緒的方式來執行。這不但為運行系統提供了良好的性能,而且使 程式設計人員擺脫了自己控制記憶體使用的風險。

JVM存儲區

JVM有兩類存儲區: 常量緩衝池和方法區。 常量緩衝池用於存儲類名稱、方法和欄位名稱以及串常量。方法區則用於存儲Java方法的 位元組碼。對於這兩種 存儲區域具體實現方式在JVM規格中沒有明確規定。這使得Java 應用程式的存儲布局必須在運行過程中確定,依賴於具體平台的實現方式。JVM是為Java 位元組碼定義的一種獨立於具體平台的規格描述,是 Java平台獨立性的基礎。儘管JVM還存在一些限制和不足,有待於進一步的完善,但無論如何,JVM的思想是成功的。對比分析:如果把Java原程式想像成我們的C++原程式,Java原程式編譯後生成的 位元組碼就相當於C++原程式編譯後的80x86的 機器碼(二進制 程式檔案),JVM 虛擬機相當於80x86 計算機系統,Java 解釋器相當於80x86CPU。在80x86CPU上運行的是 機器碼,在Java 解釋器上運行的是Java位元組碼。

Java解釋器相當於運行Java 位元組碼的“CPU”,但該“CPU”不是通過硬體實現的,而是用 軟體實現的。Java 解釋器實際上就是特定的平台下的一個 應用程式。只要實現了特定平台下的 解釋器程式,Java 位元組碼就能通過解釋器程式在該平台下運行,這是Java跨平台的根本。當前,並不是在所有的平台下都有相應Java 解釋器程式,這也是Java並不能在所有的平台下都能運行的原因,它只能在已實現了Java解釋器程式的平台下運行。

體系結構

Java 虛擬機由五個部分組成:一組 指令集、一組 暫存器、一個 棧、一個無用單元收集堆(Garbage-collected-heap)、一個方法區域。這五部分是Java 虛擬機的邏輯成份,不依賴任何實現技術或組織方式,但它們的功能必須在真實機器上以某種方式實現。

Java指令集

Java 虛擬機支持大約248個 位元組碼。每個 位元組碼執行一種基本的CPU運算,例如,把一個整數加到 暫存器, 子程式轉移等。Java 指令集相當於Java程式的彙編語言。

Java 指令集中的指令包含一個單位元組的操作符,用於指定要執行的操作,還有0個或多個 運算元,提供操作所需的參數或數據。許多指令沒有 運算元,僅由一個單位元組的操作符構成。

虛擬機的內層循環的執行過程如下:

do{

取一個操作符位元組;

根據操作符的值執行一個動作;

}while(程式未結束)

由於 指令系統的簡單性,使得 虛擬機執行的過程十分簡單,從而有利於提高執行的效率。指令中 運算元的數量和大小是由操作符決定的。如果 運算元比一個 位元組大,那么它存儲的順序是高位位元組優先。例如,一個16位的參數存放時占用兩個位元組,其值為:

第一個位元組*256+第二個位元組 位元組碼 指令流一般只是 位元組對齊的。指令tabltch和lookup是例外,在這兩條指令內部要求強制的4位元組 邊界對齊。

暫存器

Java 虛擬機的 暫存器用於保存機器的運行狀態,與微處理器中的某些專用暫存器類似。

Java 虛擬機的 暫存器有四種:

pc:Java 程式計數器。

optop:指向 運算元棧頂端的 指針。

frame:指向當前執行方法的執行環境的 指針。

vars:指向當前執行方法的 局部變數區第一個變數的 指針。

Java 虛擬機的棧有三個區域: 局部變數區、運行環境區、 運算元區。

⑴ 局部變數區 每個Java方法使用一個固定大小的局部變數集。它們按照與vars 暫存器的字 偏移量來定址。 局部變數都是32位的。長整數和 雙精度浮點數占據了兩個 局部變數的空間,卻按照第一個局部變數的索引來定址。(例如,一個具有索引n的 局部變數,如果是一個 雙精度浮點數,那么它實際占據了索引n和n+1所代表的 存儲空間。) 虛擬機規範並不要求在 局部變數中的64位的值是64位對齊的。 虛擬機提供了把 局部變數中的值裝載到 運算元棧的指令,也提供了把運算元棧中的值寫入局部變數的指令。

⑵運行環境區 在運行環境中包含的信息用於 動態連結,正常的方法返回以及異常傳播。

動態連結

運行環境包括對指向當前類和當前方法的 解釋器 符號表的 指針,用於支持方法代碼的 動態連結。方法的 class檔案代碼在引用要調用的方法和要訪問的 變數時使用符號。 動態連結把符號形式的方法調用翻譯成實際方法調用,裝載必要的類以解釋還沒有定義的符號,並把 變數訪問翻譯成與這些變數運行時的 存儲結構相應的 偏移地址。 動態連結方法和 變數使得方法中使用的其它類的變化不會影響到本程式的代碼。

正常的方法返回

如果當前方法正常地結束了,在執行了一條具有正確類型的返回指令時,調用的方法會得到一個返回值。執行環境在正常返回的情況下用於恢復調用者的 暫存器,並把調用者的 程式計數器增加一個恰當的數值,以跳過已執行過的方法調用指令,然後在調用者的執行環境中繼續執行下去。

異常和錯誤傳播

異常情況在Java中被稱作Error(錯誤)或Exception(異常),是Throwable類的子類,在程式中的原因是:① 動態連結錯,如無法找到所需的 class檔案。②運行時錯,如對一個空 指針的引用

程式使用了throw語句。

當異常發生時,Java 虛擬機採取如下措施:

檢查與當前方法相聯繫的catch子句表。每個catch子句包含其有效指令範圍,能夠處理的異常類型,以及處理異常的代碼塊地址。

與異常相匹配的catch子句應該符合下面的條件:造成異常的指令在其指令範圍之內,發生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那么系統轉移到指定的 異常處理塊處執行;如果沒有找到異常處理塊,重複尋找匹配的catch子句的過程,直到當前方法的所有嵌套的catch子句都被檢查過。

由於虛擬機從第一個匹配的catch子句處繼續執行,所以catch子句表中的順序是很重要的。因為Java代碼是結構化的,因此總可以把某個方法的所有的 異常處理器都按序排列到一個表中,對任意可能的 程式計數器的值,都可以用線性的順序找到合適的異常處

如果找不到匹配的catch子句,那么當前方法得到一個"未截獲異常"的結果並返回到當前方法的調用者,好像異常剛剛在其調用者中發生一樣。如果在調用者中仍然沒有找到相應的 異常處理塊,那么這種錯誤傳播將被繼續下去。如果錯誤被傳播到最頂層,那么系統將調用一個預設的 異常處理塊。

⑶ 運算元棧區 機器指令只從運算元棧中取運算元,對它們進行操作,並把結果返回到棧中。選擇棧結構的原因是:在只有少量 暫存器或非 通用暫存器的機器(如Intel486)上,也能夠高效地模擬 虛擬機的行為。 運算元棧是32位的。它用於給方法傳遞參數,並從方法接收結果,也用於支持操作的參數,並保存操作的結果。例如,iadd指令將兩個整數相加。相加的兩個整數應該是 運算元棧頂的兩個字。這兩個字是由先前的指令壓進堆疊的。這兩個整數將從堆疊彈出、相加,並把結果壓回到 運算元棧中。

每個原始 數據類型都有專門的指令對它們進行必須的操作。每個 運算元在棧中需要一個存儲位置,除了long和double型,它們需要兩個位置。 運算元只能被適用於其類型的操作符所操作。例如,壓入兩個int類型的數,如果把它們當作是一個long類型的數則是非法的。在Sun的 虛擬機實現中,這個限制由 位元組碼驗證器強制實行。但是,有少數操作(操作符dupe和swap),用於對運行時數據區進行操作時是不考慮類型的。

無用單元收集堆

Java的堆是一個運行時數據區,類的實例(對象)從中分配空間。Java語言具有無用單元收集能力:它不給程式設計師顯式釋放對象的能力。Java不規定具體使用的無用單元收集算法,可以根據系統的需求使用各種各樣的算法。

方法區

方法區與傳統語言中的編譯後代碼或是Unix進程中的正文段類似。它保存方法代碼(編譯後的java代碼)和 符號表。在當前的Java實現中,方法代碼不包括在無用單元收集堆中,但計畫在將來的版本中實現。每個類檔案包含了一個Java類或一個Java界面的編譯後的代碼。可以說類檔案是Java語言的執行代碼檔案。為了保證類檔案的平台無關性,Java 虛擬機規範中對類檔案的格式也作了詳細的說明。其具體細節請參考Sun公司的Java虛擬機規範。

運行過程

java虛擬機java虛擬機

上面對虛擬機的各個部分進行了比較詳細的說明,下面通過一個具體的例子來分析它的運行過程。

虛擬機通過調用某個指定類的方法main啟動,傳遞給main一個字元串數組參數,使指定的類被裝載,同時連結該類所使用的其它的類型,並且初始化它們。例如對於程式:

public class HelloApp {

public static void main(String[] args){

System.out.println("Hello World!");

for (int i = 0; i < args.length; i++ ) {

System.out.println(args);

}

}

}

編譯後在命令行模式下鍵入:java HelloApp run virtual machine

將通過調用HelloApp的方法main來啟動java虛擬機,傳遞給main一個包含三個字元串"run"、"virtual"、"machine"的數組。現在我們略述虛擬機在執行HelloApp時可能採取的步驟。

開始試圖執行類HelloApp的main方法,發現該類並沒有被裝載,也就是說虛擬機當前不包含該類的二進制代表,於是虛擬機使用ClassLoader試圖尋找這樣的二進制代表。如果這個進程失敗,則拋出一個異常。類被裝載後同時在main方法被調用之前,必須對類HelloApp與其它類型進行連結然後初始化。連結包含三個階段:檢驗,準備和解析。檢驗檢查被裝載的主類的符號和語義,準備則創建類或接口的靜態域以及把這些域初始化為標準的默認值,解析負責檢查主類對其它類或接口的符號引用,在這一步它是可選的。類的初始化是對類中聲明的靜態初始化函式和靜態域的初始化構造方法的執行。一個類在初始化之前它的父類必須被初始化。整個過程如下:

參數說明

一、運行 class檔案

執行帶main方法的 class檔案,Java 虛擬機 命令參數行為:

java

注意:CLASS檔案名稱不要帶檔案後綴.class

例如:

java Test

如果執行的 class檔案是帶包的,即在類檔案中使用了:

package <;包名>

那應該在包的基路徑下執行,Java虛擬機 命令行參數:

java <;包名>.CLASS檔案名稱

例如:

PackageTest.java中,其包名為:com.ee2ee.test,對應的語句為:

package com.ee2ee.test;

PackageTest.java及編譯後的 class檔案PackageTest.class的存放目錄如下:

classes

|__com

|__ee2ee

|__test

|__PackageTest.java

|__PackageTest.class

要運行PackageTest.class,應在classes目錄下執行:

java com.ee2ee.test.PackageTest

二、運行jar檔案中的class

原理和運行 class檔案一樣,只需加上參數-cp ;即可。

例如:執行test.jar中的類com.ee2ee.test.PackageTest,命令行如下:

java -cp test.jar com.ee2ee.test.PackageTest

三、顯示JDK版本信息

當一台機器上有多個jdk版本時,需要知道當前使用的是那個版本的jdk,使用參數-version即可知道其版本,命令行為:

java -version

四、增加虛擬機可以使用的最大記憶體

Java 虛擬機可使用的最大記憶體是有限制的,預設值通常為64MB或128MB。

如果一個 應用程式為了提高性能而把數據載入記憶體中而占用較大的記憶體, 比如超過了默認的最大值128MB,需要加大java 虛擬機可使用的最大記憶體,否則會出現Out of Memory( 系統記憶體不足)的異常。啟動java時,需要使用如下兩個參數:

-Xms java 虛擬機初始化時使用的記憶體大小

-Xmx java 虛擬機可以使用的最大記憶體

以上兩個 命令行參數中設定的size,可以帶單位,例如:256m表示256MB

舉例說明:

java -Xms128m -Xmx256m ...

表示Java 虛擬機初始化時使用的記憶體為128MB,可使用的最大記憶體為256MB。

對於tomcat,可以修改其 腳本catalina. sh(Unix平台)或catalina.bat(Windows平台),設定 變數JAVA_OPTS即可,例如:

JAVA_OPTS="-Xms128m -Xmx256m"

相關詞條

相關搜尋

熱門詞條

聯絡我們