天天看點

java虛拟機

<dl></dl>

<dd></dd>

java虛拟機

     虛拟機是一種抽象化的計算機,通過在實際的計算機上仿真模拟各種計算機功能來實作的。Java虛拟機有自己完善的硬體架構,如

處理器、

堆棧、

寄存器等,還具有相應的

指令系統。JVM屏蔽了與具體作業系統平台相關的資訊,使得Java程式隻需生成在Java虛拟機上運作的目标代碼(

位元組碼),就可以在多種平台上不加修改地運作。

      Java

虛拟機(Java Virtual Machine 簡稱JVM)是運作所有Java程式的抽象計算機,是

Java語言的運作環境,它是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虛拟機安裝方法

下載下傳解壓:

     下載下傳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回車

     JVM的設計目标是提供一個基于抽象規格描述的計算機模型,為解釋程式開發人員提範的任何系統上運作。JVM對其實作的某些方面給出了具體的定義,特别是對Java

可執行代碼,即

位元組碼(Bytecode)的格式給出了明确的規格。這一規格包括

操作碼和

操作數的文法和數值、

辨別符的數值表示方式、以及Java類檔案中的Java對象、

常量緩沖池在JVM的存儲映象。這些定義為JVM

解釋器開發人員提供了所需的資訊和開發環境。Java的設計者希望給開發人員以随心所欲使用Java的自由。

JVM定義了控制Java代碼

解釋執行和具體實作的五種規格,它們是:

*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多種操作碼。

      所有的CPU均包含用于儲存系統狀态和處理器所需資訊的

寄存器組。如果

虛拟機定義較多的

寄存器,便可以從中得到更多的資訊而不必對棧或記憶體進行通路,這有利于提高運作速度。然而,如果虛拟機中的寄存器比實際CPU的寄存器多,在實作虛拟機時就會占用處理器大量的時間來用正常

存儲器模拟寄存器,這反而會降低虛拟機的效率。針對這種情況,JVM隻設定了4個最為常用的寄存器。它們是:pc

程式計數器optop

操作數棧頂

指針frame目前執行環境指針 vars指向目前執行環境中第一個

局部變量的指針 所有寄存器均為32位。pc用于記錄程式的執行。optop,frame和vars用于記錄指向Java棧區的指針。

       作為基于棧結構的計算機,Java棧是JVM存儲資訊的主要方法。當JVM得到一個Java

位元組碼應用程式後,便為該代碼中一個類的每一個方法建立一個棧架構,以儲存該方法的狀态資訊。每個棧架構包括以下三類資訊:局部變量,執行環境,

操作數棧。

局部變量用于存儲一個類的方法中所用到的局部變量。vars

寄存器指向該變量表中的第一個局部變量。

執行環境用于儲存

解釋器對Java位元組碼進行解釋過程中所需的資訊。它們是:上次調用的方法、局部變量

指針和操作數棧的棧頂和棧底指針。執行環境是一個執行一個方法的控制中心。例如:如果解釋器要執行iadd(整數加法),首先要從frame寄存器中找到目前執行環境,而後便從執行環境中找到

操作數棧,從棧頂彈出兩個整數進行加法運算,最後将結果壓入棧頂。

操作數棧用于存儲運算所需操作數及運算的結果。

      Java類的執行個體所需的存儲空間是在堆上配置設定的。

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

       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解釋器程式的平台下運作。

虛拟機由五個部分組成:一組

指令集、一組

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

虛拟機支援大約248個

位元組碼。每個位元組碼執行一種基本的CPU運算,例如,把一個整數加到

寄存器,

子程式轉移等。Java

指令集相當于Java程式的彙編語言。

Java指令集中的指令包含一個單位元組的操作符,用于指定要執行的操作,還有0個或多個

操作數,提供操作所需的參數或資料。許多指令沒有操作數,僅由一個單位元組的操作符構成。

虛拟機的内層循環的執行過程如下:

do{

取一個操作符位元組;

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

}while(程式未結束)

由于

指令系統的簡單性,使得

虛拟機執行的過程十分簡單,進而有利于提高執行的效率。指令中

操作數的數量和大小是由操作符決定的。如果操作數比一個

位元組大,那麼它存儲的順序是高位位元組優先。例如,一個16位的參數存放時占用兩個位元組,其值為:

第一個位元組*256+第二個位元組

位元組碼

指令流一般隻是

位元組對齊的。指令tabltch和lookup是例外,在這兩條指令内部要求強制的4位元組

邊界對齊。

       Java

虛拟機的

寄存器用于儲存機器的運作狀态,與微處理器中的某些專用寄存器類似。

Java虛拟機的寄存器有四種:

pc:Java

程式計數器。

optop:指向

操作數棧頂端的

指針。

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

vars:指向目前執行方法的

局部變量區第一個變量的指針。

虛拟機的棧有三個區域:

局部變量區、運作環境區、

操作數區。

⑴局部變量區 每個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虛拟機規範。

       上面對

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

虛拟機通過調用某個指定類的方法main啟動,傳遞給main一個字元串

數組參數,使指定的類被裝載,同時連結該類所使用的其它的類型,并且初始化它們。例如對于程式:

public class HelloApp {

public static void main(String[] args){

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

for (int i = 0; i &lt; 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

虛拟機[3]

  指令參數行為:

java &lt;CLASS檔案名&gt;

注意:CLASS檔案名不要帶檔案字尾.class

例如:

java Test

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

package &lt;;包名&gt;

那應該在包的基路徑下執行,Java虛拟機

指令行參數:

java &lt;;包名&gt;.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 &lt;jar檔案名&gt;;即可。

例如:執行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'

上一篇: Java虛拟機
下一篇: Java虛拟機

繼續閱讀