真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java虛擬機(jī)代碼塊 Java虛擬機(jī)源碼

Java中虛擬機(jī)為靜態(tài)代碼塊,靜態(tài)方法,靜態(tài)變量分配的內(nèi)存什么時(shí)候被回收?

參考如下解釋:

創(chuàng)新互聯(lián)公司服務(wù)項(xiàng)目包括盧氏網(wǎng)站建設(shè)、盧氏網(wǎng)站制作、盧氏網(wǎng)頁制作以及盧氏網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,盧氏網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到盧氏省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

Static方法 并沒有完全分配內(nèi)存,只是把類的方法或?qū)傩缘男畔⒓尤肓藘?nèi)存中,這樣訪問起來就會(huì)比較快,

當(dāng)程序開始運(yùn)行時(shí),Static就分配好了,釋放要到程序結(jié)束時(shí)。中間,它一直活著

java中的static

有要詳解,那我找資料給你吧

static表示“全局”或者“靜態(tài)”的意思,用來修飾成員變量和成員方法,也可以形成靜態(tài)static代碼塊,但是Java語言中沒有全局變量的概念。

被static修飾的成員變量和成員方法獨(dú)立于該類的任何對(duì)象。也就是說,它不依賴類特定的實(shí)例,被類的所有實(shí)例共享。

只要這個(gè)類被加載,Java虛擬機(jī)就能根據(jù)類名在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)定找到他們。因此,static對(duì)象可以在它的任何對(duì)象創(chuàng)建之前訪問,無需引用任何對(duì)象。

用public修飾的static成員變量和成員方法本質(zhì)是全局變量和全局方法,當(dāng)聲明它類的對(duì)象市,不生成static變量的副本,而是類的所有實(shí)例共享同一個(gè)static變量。

static變量前可以有private修飾,表示這個(gè)變量可以在類的靜態(tài)代碼塊中,或者類的其他靜態(tài)成員方法中使用(當(dāng)然也可以在非靜態(tài)成員方法中使用--廢話),但是不能在其他類中通過類名來直接引用,這一點(diǎn)很重要。實(shí)際上你需要搞明白,private是訪問權(quán)限限定,static表示不要實(shí)例化就可以使用,這樣就容易理解多了。static前面加上其它訪問權(quán)限關(guān)鍵字的效果也以此類推。

static修飾的成員變量和成員方法習(xí)慣上稱為靜態(tài)變量和靜態(tài)方法,可以直接通過類名來訪問,訪問語法為:

類名.靜態(tài)方法名(參數(shù)列表...)

類名.靜態(tài)變量名

用static修飾的代碼塊表示靜態(tài)代碼塊,當(dāng)Java虛擬機(jī)(JVM)加載類時(shí),就會(huì)執(zhí)行該代碼塊(用處非常大,呵呵)。

1、static變量

 按照是否靜態(tài)的對(duì)類成員變量進(jìn)行分類可分兩種:一種是被static修飾的變量,叫靜態(tài)變量或類變量;另一種是沒有被static修飾的變量,叫實(shí)例變量。

兩者的區(qū)別是:

 對(duì)于靜態(tài)變量在內(nèi)存中只有一個(gè)拷貝(節(jié)省內(nèi)存),JVM只為靜態(tài)分配一次內(nèi)存,在加載類的過程中完成靜態(tài)變量的內(nèi)存分配,可用類名直接訪問(方便),當(dāng)然也可以通過對(duì)象來訪問(但是這是不推薦的)。

 對(duì)于實(shí)例變量,沒創(chuàng)建一個(gè)實(shí)例,就會(huì)為實(shí)例變量分配一次內(nèi)存,實(shí)例變量可以在內(nèi)存中有多個(gè)拷貝,互不影響(靈活)。

所以一般在需要實(shí)現(xiàn)以下兩個(gè)功能時(shí)使用靜態(tài)變量:

? 在對(duì)象之間共享值時(shí)

? 方便訪問變量時(shí)

2、靜態(tài)方法

靜態(tài)方法可以直接通過類名調(diào)用,任何的實(shí)例也都可以調(diào)用,

因此靜態(tài)方法中不能用this和super關(guān)鍵字,不能直接訪問所屬類的實(shí)例變量和實(shí)例方法(就是不帶static的成員變量和成員成員方法),只能訪問所屬類的靜態(tài)成員變量和成員方法。

因?yàn)閷?shí)例成員與特定的對(duì)象關(guān)聯(lián)!這個(gè)需要去理解,想明白其中的道理,不是記憶?。?!

因?yàn)閟tatic方法獨(dú)立于任何實(shí)例,因此static方法必須被實(shí)現(xiàn),而不能是抽象的abstract。

例如為了方便方法的調(diào)用,Java API中的Math類中所有的方法都是靜態(tài)的,而一般類內(nèi)部的static方法也是方便其它類對(duì)該方法的調(diào)用。

靜態(tài)方法是類內(nèi)部的一類特殊方法,只有在需要時(shí)才將對(duì)應(yīng)的方法聲明成靜態(tài)的,一個(gè)類內(nèi)部的方法一般都是非靜態(tài)的

3、static代碼塊

 static代碼塊也叫靜態(tài)代碼塊,是在類中獨(dú)立于類成員的static語句塊,可以有多個(gè),位置可以隨便放,它不在任何的方法體內(nèi),JVM加載類時(shí)會(huì)執(zhí)行這些靜態(tài)的代碼塊,如果static代碼塊有多個(gè),JVM將按照它們?cè)陬愔谐霈F(xiàn)的先后順序依次執(zhí)行它們,每個(gè)代碼塊只會(huì)被執(zhí)行一次。例如:

public class Test5 {

private static int a;

private int b;

static{

Test5.a=3;

System.out.println(a);

Test5 t=new Test5();

t.f();

t.b=1000;

System.out.println(t.b);

}

static{

Test5.a=4;

System.out.println(a);

}

public static void main(String[] args) {

// TODO 自動(dòng)生成方法存根

}

static{

Test5.a=5;

System.out.println(a);

}

public void f(){

System.out.println("hhahhahah");

}

}

運(yùn)行結(jié)果:

3

hhahhahah

1000

4

5

 利用靜態(tài)代碼塊可以對(duì)一些static變量進(jìn)行賦值,最后再看一眼這些例子,都一個(gè)static的main方法,這樣JVM在運(yùn)行main方法的時(shí)候可以直接調(diào)用而不用創(chuàng)建實(shí)例。

4、static和final一塊用表示什么

static final用來修飾成員變量和成員方法,可簡(jiǎn)單理解為“全局常量”!

對(duì)于變量,表示一旦給值就不可修改,并且通過類名可以訪問。

對(duì)于方法,表示不可覆蓋,并且可以通過類名直接訪問。

有時(shí)你希望定義一個(gè)類成員,使它的使用完全獨(dú)立于該類的任何對(duì)象。通常情況下,類成員必須通過它的類的對(duì)象訪問,但是可以創(chuàng)建這樣一個(gè)成員,它能夠被它自己使用,而不必引用特定的實(shí)例。在成員的聲明前面加上關(guān)鍵字static(靜態(tài)的)就能創(chuàng)建這樣的成員。如果一個(gè)成員被聲明為static,它就能夠在它的類的任何對(duì)象創(chuàng)建之前被訪問,而不必引用任何對(duì)象。你可以將方法和變量都聲明為static。static 成員的最常見的例子是main( ) 。因?yàn)樵诔绦蜷_始執(zhí)行時(shí)必須調(diào)用main() ,所以它被聲明為static。

聲明為static的變量實(shí)質(zhì)上就是全局變量。當(dāng)聲明一個(gè)對(duì)象時(shí),并不產(chǎn)生static變量的拷貝,而是該類所有的實(shí)例變量共用同一個(gè)static變量。聲明為static的方法有以下幾條限制:

?

它們僅能調(diào)用其他的static 方法。

?

它們只能訪問static數(shù)據(jù)。

?

它們不能以任何方式引用this 或super(關(guān)鍵字super 與繼承有關(guān),在下一章中描述)。

如果你需要通過計(jì)算來初始化你的static變量,你可以聲明一個(gè)static塊,Static 塊僅在該類被加載時(shí)執(zhí)行一次。下面的例子顯示的類有一個(gè)static方法,一些static變量,以及一個(gè)static 初始化塊:

// Demonstrate static variables,methods,and blocks.

class UseStatic {

static int a = 3;

static int b;

static void meth(int x) {

System.out.println("x = " + x);

System.out.println("a = " + a);

System.out.println("b = " + b);

}

static {

System.out.println("Static block initialized.");

b = a * 4;

}

public static void main(String args[]) {

meth(42);

}

}

一旦UseStatic 類被裝載,所有的static語句被運(yùn)行。首先,a被設(shè)置為3,接著static 塊執(zhí)行(打印一條消息),最后,b被初始化為a*4 或12。然后調(diào)用main(),main() 調(diào)用meth() ,把值42傳遞給x。3個(gè)println ( ) 語句引用兩個(gè)static變量a和b,以及局部變量x 。

注意:在一個(gè)static 方法中引用任何實(shí)例變量都是非法的。

下面是該程序的輸出:

Static block initialized.

x = 42

a = 3

b = 12

在定義它們的類的外面,static 方法和變量能獨(dú)立于任何對(duì)象而被使用。這樣,你只要在類的名字后面加點(diǎn)號(hào)運(yùn)算符即可。例如,如果你希望從類外面調(diào)用一個(gè)static方法,你可以使用下面通用的格式:

classname.method( )

這里,classname 是類的名字,在該類中定義static方法。可以看到,這種格式與通過對(duì)象引用變量調(diào)用非static方法的格式類似。一個(gè)static變量可以以同樣的格式來訪問——類名加點(diǎn)號(hào)運(yùn)算符。這就是Java 如何實(shí)現(xiàn)全局功能和全局變量的一個(gè)控制版本。

下面是一個(gè)例子。在main() 中,static方法callme() 和static 變量b在它們的類之外被訪問。

class StaticDemo {

static int a = 42;

static int b = 99;

static void callme() {

System.out.println("a = " + a);

}

}

class StaticByName {

public static void main(String args[]) {

StaticDemo.callme();

System.out.println("b = " + StaticDemo.b);

}

}

下面是該程序的輸出:

a = 42

b = 99

static成員是不能被其所在class創(chuàng)建的實(shí)例訪問的。

如果不加static修飾的成員是對(duì)象成員,也就是歸每個(gè)對(duì)象所有的。

加static修飾的成員是類成員,就是可以由一個(gè)類直接調(diào)用,為所有對(duì)象共有的

這樣可以么?

什么是 Java 虛擬機(jī)?

您好,提問者:

Java虛擬機(jī)簡(jiǎn)稱JVM,它的作用如下:

1、其實(shí)Java不可跨平臺(tái),真正實(shí)現(xiàn)跨平臺(tái)的是JVM虛擬機(jī)。

2、JVM其實(shí)就是一個(gè)編譯java、運(yùn)行class的一個(gè)跟操作系統(tǒng)的一個(gè)軟件。

3、JVM的作用只針對(duì)于Java,而系統(tǒng)中的東西與它無關(guān)。

4、其實(shí)說白了就是一個(gè)軟件,就像VMware一樣。

Java虛擬機(jī)

一、什么是Java虛擬機(jī)

Java虛擬機(jī)是一個(gè)想象中的機(jī)器,在實(shí)際的計(jì)算機(jī)上通過軟件模擬來實(shí)現(xiàn)。Java虛擬機(jī)有自己想象中的硬件,如處理器、堆棧、寄存器等,還具有相應(yīng)的指令系統(tǒng)。

為什么要使用Java虛擬機(jī)

Java語言的一個(gè)非常重要的特點(diǎn)就是與平臺(tái)的無關(guān)性。而使用Java虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。一般的高級(jí)語言如果要在不同的平臺(tái)上運(yùn)行,至少需要編譯成不同的目標(biāo)代碼。而引入Java語言虛擬機(jī)后,Java語言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。Java語言使用模式Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。Java虛擬機(jī)在執(zhí)行字節(jié)碼時(shí),把字節(jié)碼解釋成具體平臺(tái)上的機(jī)器指令執(zhí)行。?

2.誰需要了解Java虛擬機(jī)

Java虛擬機(jī)是Java語言底層實(shí)現(xiàn)的基礎(chǔ),對(duì)Java語言感興趣的人都應(yīng)對(duì)Java虛擬機(jī)有個(gè)大概的了解。這有助于理解Java語言的一些性質(zhì),也有助于使用Java語言。對(duì)于要在特定平臺(tái)上實(shí)現(xiàn)Java虛擬機(jī)的軟件人員,Java語言的編譯器作者以及要用硬件芯片實(shí)現(xiàn)Java虛擬機(jī)的人來說,則必須深刻理解Java虛擬機(jī)的規(guī)范。另外,如果你想擴(kuò)展Java語言,或是把其它語言編譯成Java語言的字節(jié)碼,你也需要深入地了解Java虛擬機(jī)。

3.Java虛擬機(jī)支持的數(shù)據(jù)類型

Java虛擬機(jī)支持Java語言的基本數(shù)據(jù)類型如下:

byte://1字節(jié)有符號(hào)整數(shù)的補(bǔ)碼

short://2字節(jié)有符號(hào)整數(shù)的補(bǔ)碼

int://4字節(jié)有符號(hào)整數(shù)的補(bǔ)碼

long://8字節(jié)有符號(hào)整數(shù)的補(bǔ)碼

float://4字節(jié)IEEE754單精度浮點(diǎn)數(shù)

double://8字節(jié)IEEE754雙精度浮點(diǎn)數(shù)

char://2字節(jié)無符號(hào)Unicode字符

幾乎所有的Java類型檢查都是在編譯時(shí)完成的。上面列出的原始數(shù)據(jù)類型的數(shù)據(jù)在Java執(zhí)行時(shí)不需要用硬件標(biāo)記。操作這些原始數(shù)據(jù)類型數(shù)據(jù)的字節(jié)碼(指令)本身就已經(jīng)指出了操作數(shù)的數(shù)據(jù)類型,例如iadd、ladd、fadd和dadd指令都是把兩個(gè)數(shù)相加,其操作數(shù)類型別是int、long、float和double。虛擬機(jī)沒有給boolean(布爾)類型設(shè)置單獨(dú)的指令。boolean型的數(shù)據(jù)是由integer指令,包括integer返回來處理的。boolean型的數(shù)組則是用byte數(shù)組來處理的。虛擬機(jī)使用IEEE754格式的浮點(diǎn)數(shù)。不支持IEEE格式的較舊的計(jì)算機(jī),在運(yùn)行Java數(shù)值計(jì)算程序時(shí),可能會(huì)非常慢。

虛擬機(jī)支持的其它數(shù)據(jù)類型包括:

object//對(duì)一個(gè)Javaobject(對(duì)象)的4字節(jié)引用

returnAddress//4字節(jié),用于jsr/ret/jsr-w/ret-w指令

注:Java數(shù)組被當(dāng)作object處理。

虛擬機(jī)的規(guī)范對(duì)于object內(nèi)部的結(jié)構(gòu)沒有任何特殊的要求。在Sun公司的實(shí)現(xiàn)中,對(duì)object的引用是一個(gè)句柄,其中包含一對(duì)指針:一個(gè)指針指向該object的方法表,另一個(gè)指向該object的數(shù)據(jù)。用Java虛擬機(jī)的字節(jié)碼表示的程序應(yīng)該遵守類型規(guī)定。Java虛擬機(jī)的實(shí)現(xiàn)應(yīng)拒絕執(zhí)行違反了類型規(guī)定的字節(jié)碼程序。Java虛擬機(jī)由于字節(jié)碼定義的限制似乎只能運(yùn)行于32位地址空間的機(jī)器上。但是可以創(chuàng)建一個(gè)Java虛擬機(jī),它自動(dòng)地把字節(jié)碼轉(zhuǎn)換成64位的形式。從Java虛擬機(jī)支持的數(shù)據(jù)類型可以看出,Java對(duì)數(shù)據(jù)類型的內(nèi)部格式進(jìn)行了嚴(yán)格規(guī)定,這樣使得各種Java虛擬機(jī)的實(shí)現(xiàn)對(duì)數(shù)據(jù)的解釋是相同的,從而保證了Java的與平臺(tái)無關(guān)性和可

移植性。

二、Java虛擬機(jī)體系結(jié)構(gòu)

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

Java指令集

Java虛擬機(jī)支持大約248個(gè)字節(jié)碼。每個(gè)字節(jié)碼執(zhí)行一種基本的CPU運(yùn)算,例如,把一個(gè)整數(shù)加到寄存器,子程序轉(zhuǎn)移等。Java指令集相當(dāng)于Java程序的匯編語言。?

Java指令集中的指令包含一個(gè)單字節(jié)的操作符,用于指定要執(zhí)行的操作,還有0個(gè)或多個(gè)操作數(shù),提供操作所需的參數(shù)或數(shù)據(jù)。許多指令沒有操作數(shù),僅由一個(gè)單字節(jié)的操作符構(gòu)成。

虛擬機(jī)的內(nèi)層循環(huán)的執(zhí)行過程如下:

do{

取一個(gè)操作符字節(jié);

根據(jù)操作符的值執(zhí)行一個(gè)動(dòng)作;

}while(程序未結(jié)束)

由于指令系統(tǒng)的簡(jiǎn)單性,使得虛擬機(jī)執(zhí)行的過程十分簡(jiǎn)單,從而有利于提高執(zhí)行的效率。指令中操作數(shù)的數(shù)量和大小是由操作符決定的。如果操作數(shù)比一個(gè)字節(jié)大,那么它存儲(chǔ)的順序是高位字節(jié)優(yōu)先。例如,一個(gè)16位的參數(shù)存放時(shí)占用兩個(gè)字節(jié),其值為:

第一個(gè)字節(jié)*256+第二個(gè)字節(jié)字節(jié)碼指令流一般只是字節(jié)對(duì)齊的。指令tabltch和lookup是例外,在這兩條指令內(nèi)部要求強(qiáng)制的4字節(jié)邊界對(duì)齊。

2.寄存器

Java虛擬機(jī)的寄存器用于保存機(jī)器的運(yùn)行狀態(tài),與微處理器中的某些專用寄存器類似。

Java虛擬機(jī)的寄存器有四種:

pc:Java程序計(jì)數(shù)器。

optop:指向操作數(shù)棧頂端的指針。

frame:指向當(dāng)前執(zhí)行方法的執(zhí)行環(huán)境的指針。

vars:指向當(dāng)前執(zhí)行方法的局部變量區(qū)第一個(gè)變量的指針。

Java虛擬機(jī)

Java虛擬機(jī)是棧式的,它不定義或使用寄存器來傳遞或接受參數(shù),其目的是為了保證指令集的簡(jiǎn)潔性和實(shí)現(xiàn)時(shí)的高效性(特別是對(duì)于寄存器數(shù)目不多的處理器)。

所有寄存器都是32位的。

3.棧

Java虛擬機(jī)的棧有三個(gè)區(qū)域:局部變量區(qū)、運(yùn)行環(huán)境區(qū)、操作數(shù)區(qū)。

(1)局部變量區(qū) 每個(gè)Java方法使用一個(gè)固定大小的局部變量集。它們按照與vars寄存器的字偏移量來尋址。局部變量都是32位的。長(zhǎng)整數(shù)和雙精度浮點(diǎn)數(shù)占據(jù)了兩個(gè)局部變量的空間,卻按照第一個(gè)局部變量的索引來尋址。(例如,一個(gè)具有索引n的局部變量,如果是一個(gè)雙精度浮點(diǎn)數(shù),那么它實(shí)際占據(jù)了索引n和n+1所代表的存儲(chǔ)空間。)虛擬機(jī)規(guī)范并不要求在局部變量中的64位的值是64位對(duì)齊的。虛擬機(jī)提供了把局部變量中的值裝載到操作數(shù)棧的指令,也提供了把操作數(shù)棧中的值寫入局部變量的指令。

(2)運(yùn)行環(huán)境區(qū) 在運(yùn)行環(huán)境中包含的信息用于動(dòng)態(tài)鏈接,正常的方法返回以及異常傳播。

·動(dòng)態(tài)鏈接

運(yùn)行環(huán)境包括對(duì)指向當(dāng)前類和當(dāng)前方法的解釋器符號(hào)表的指針,用于支持方法代碼的動(dòng)態(tài)鏈接。方法的class文件代碼在引用要調(diào)用的方法和要訪問的變量時(shí)使用符號(hào)。動(dòng)態(tài)鏈接把符號(hào)形式的方法調(diào)用翻譯成實(shí)際方法調(diào)用,裝載必要的類以解釋還沒有定義的符號(hào),并把變量訪問翻譯成與這些變量運(yùn)行時(shí)的存儲(chǔ)結(jié)構(gòu)相應(yīng)的偏移地址。動(dòng)態(tài)鏈接方法和變量使得方法中使用的其它類的變化不會(huì)影響到本程序的代碼。

·正常的方法返回

如果當(dāng)前方法正常地結(jié)束了,在執(zhí)行了一條具有正確類型的返回指令時(shí),調(diào)用的方法會(huì)得到一個(gè)返回值。執(zhí)行環(huán)境在正常返回的情況下用于恢復(fù)調(diào)用者的寄存器,并把調(diào)用者的程序計(jì)數(shù)器增加一個(gè)恰當(dāng)?shù)臄?shù)值,以跳過已執(zhí)行過的方法調(diào)用指令,然后在調(diào)用者的執(zhí)行環(huán)境中繼續(xù)執(zhí)行下去。

·異常和錯(cuò)誤傳播

異常情況在Java中被稱作Error(錯(cuò)誤)或Exception(異常),是Throwable類的子類,在程序中的原因是:①動(dòng)態(tài)鏈接錯(cuò),如無法找到所需的class文件。②運(yùn)行時(shí)錯(cuò),如對(duì)一個(gè)空指針的引用

·程序使用了throw語句。

當(dāng)異常發(fā)生時(shí),Java虛擬機(jī)采取如下措施:

·檢查與當(dāng)前方法相聯(lián)系的catch子句表。每個(gè)catch子句包含其有效指令范圍,能夠處理的異常類型,以及處理異常的代碼塊地址。

·與異常相匹配的catch子句應(yīng)該符合下面的條件:造成異常的指令在其指令范圍之內(nèi),發(fā)生的異常類型是其能處理的異常類型的子類型。如果找到了匹配的catch子句,那么系統(tǒng)轉(zhuǎn)移到指定的異常處理塊處執(zhí)行;如果沒有找到異常處理塊,重復(fù)尋找匹配的catch子句的過程,直到當(dāng)前方法的所有嵌套的catch子句都被檢查過。

·由于虛擬機(jī)從第一個(gè)匹配的catch子句處繼續(xù)執(zhí)行,所以catch子句表中的順序是很重要的。因?yàn)镴ava代碼是結(jié)構(gòu)化的,因此總可以把某個(gè)方法的所有的異常處理器都按序排列到一個(gè)表中,對(duì)任意可能的程序計(jì)數(shù)器的值,都可以用線性的順序找到合適的異常處理塊,以處理在該程序計(jì)數(shù)器值下發(fā)生的異常情況。

·如果找不到匹配的catch子句,那么當(dāng)前方法得到一個(gè)"未截獲異常"的結(jié)果并返回到當(dāng)前方法的調(diào)用者,好像異常剛剛在其調(diào)用者中發(fā)生一樣。如果在調(diào)用者中仍然沒有找到相應(yīng)的異常處理塊,那么這種錯(cuò)誤傳播將被繼續(xù)下去。如果錯(cuò)誤被傳播到最頂層,那么系統(tǒng)將調(diào)用一個(gè)缺省的異常處理塊。

(3)操作數(shù)棧區(qū) 機(jī)器指令只從操作數(shù)棧中取操作數(shù),對(duì)它們進(jìn)行操作,并把結(jié)果返回到棧中。選擇棧結(jié)構(gòu)的原因是:在只有少量寄存器或非通用寄存器的機(jī)器(如Intel486)上,也能夠高效地模擬虛擬機(jī)的行為。操作數(shù)棧是32位的。它用于給方法傳遞參數(shù),并從方法接收結(jié)果,也用于支持操作的參數(shù),并保存操作的結(jié)果。例如,iadd指令將兩個(gè)整數(shù)相加。相加的兩個(gè)整數(shù)應(yīng)該是操作數(shù)棧頂?shù)膬蓚€(gè)字。這兩個(gè)字是由先前的指令壓進(jìn)堆棧的。這兩個(gè)整數(shù)將從堆棧彈出、相加,并把結(jié)果壓回到操作數(shù)棧中。

每個(gè)原始數(shù)據(jù)類型都有專門的指令對(duì)它們進(jìn)行必須的操作。每個(gè)操作數(shù)在棧中需要一個(gè)存儲(chǔ)位置,除了long和double型,它們需要兩個(gè)位置。操作數(shù)只能被適用于其類型的操作符所操作。例如,壓入兩個(gè)int類型的數(shù),如果把它們當(dāng)作是一個(gè)long類型的數(shù)則是非法的。在Sun的虛擬機(jī)實(shí)現(xiàn)中,這個(gè)限制由字節(jié)碼驗(yàn)證器強(qiáng)制實(shí)行。但是,有少數(shù)操作(操作符dupe和swap),用于對(duì)運(yùn)行時(shí)數(shù)據(jù)區(qū)進(jìn)行操作時(shí)是不考慮類型的。

4.無用單元收集堆

Java的堆是一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū),類的實(shí)例(對(duì)象)從中分配空間。Java語言具有無用單元收集能力:它不給程序員顯式釋放對(duì)象的能力。Java不規(guī)定具體使用的無用單元收集算法,可以根據(jù)系統(tǒng)的需求使用各種各樣的算法。

5.方法區(qū)

方法區(qū)與傳統(tǒng)語言中的編譯后代碼或是Unix進(jìn)程中的正文段類似。它保存方法代碼(編譯后的java代碼)和符號(hào)表。在當(dāng)前的Java實(shí)現(xiàn)中,方法代碼不包括在無用單元收集堆中,但計(jì)劃在將來的版本中實(shí)現(xiàn)。每個(gè)類文件包含了一個(gè)Java類或一個(gè)Java界面的編譯后的代碼。可以說類文件是Java語言的執(zhí)行代碼文件。為了保證類文件的平臺(tái)無關(guān)性,Java虛擬機(jī)規(guī)范中對(duì)類文件的格式也作了詳細(xì)的說明。其具體細(xì)節(jié)請(qǐng)參考Sun公司的Java虛擬機(jī)規(guī)范。

Java 虛擬機(jī)一樣的速度甚至出現(xiàn)AOT編譯方式嗎

不論是物理機(jī)還是虛擬機(jī),大部分的程序代碼從開始編譯到最終轉(zhuǎn)化成物理機(jī)的目標(biāo)代碼或虛擬機(jī)能執(zhí)行的指令集之前,都會(huì)按照如下圖所示的各個(gè)步驟進(jìn)行:

其中綠色的模塊可以選擇性實(shí)現(xiàn)。很容易看出,上圖中間的那條分支是解釋執(zhí)行的過程(即一條字節(jié)碼一條字節(jié)碼地解釋執(zhí)行,如JavaScript),而下面的那條分支就是傳統(tǒng)編譯原理中從源代碼到目標(biāo)機(jī)器代碼的生成過程。

如今,基于物理機(jī)、虛擬機(jī)等的語言,大多都遵循這種基于現(xiàn)代經(jīng)典編譯原理的思路,在執(zhí)行前先對(duì)程序源碼進(jìn)行詞法解析和語法解析處理,把源碼轉(zhuǎn)化為抽象語法樹。對(duì)于一門具體語言的實(shí)現(xiàn)來說,詞法和語法分析乃至后面的優(yōu)化器和目標(biāo)代碼生成器都可以選擇獨(dú)立于執(zhí)行引擎,形成一個(gè)完整意義的編譯器去實(shí)現(xiàn),這類代表是C/C++語言。也可以把抽象語法樹或指令流之前的步驟實(shí)現(xiàn)一個(gè)半獨(dú)立的編譯器,這類代表是Java語言。又或者可以把這些步驟和執(zhí)行引擎全部集中在一起實(shí)現(xiàn),如大多數(shù)的JavaScript執(zhí)行器。

Javac編譯

在Java中提到“編譯”,自然很容易想到Javac編譯器將*.java文件編譯成為*.class文件的過程,這里的Javac編譯器稱為前端編譯器,其他的前端編譯器還有諸如Eclipse?JDT中的增量式編譯器ECJ等。相對(duì)應(yīng)的還有后端編譯器,它在程序運(yùn)行期間將字節(jié)碼轉(zhuǎn)變成機(jī)器碼(現(xiàn)在的Java程序在運(yùn)行時(shí)基本都是解釋執(zhí)行加編譯執(zhí)行),如HotSpot虛擬機(jī)自帶的JIT(Just?In?Time?Compiler)編譯器(分Client端和Server端)。另外,有時(shí)候還有可能會(huì)碰到靜態(tài)提前編譯器(AOT,Ahead?Of?Time?Compiler)直接把*.java文件編譯成本地機(jī)器代碼,如GCJ、Excelsior?JET等,這類編譯器我們應(yīng)該比較少遇到。

下面簡(jiǎn)要說下Javac編譯(前端編譯)的過程。

詞法、語法分析

詞法分析是將源代碼的字符流轉(zhuǎn)變?yōu)闃?biāo)記(Token)集合。單個(gè)字符是程序編寫過程中的的最小元素,而標(biāo)記則是編譯過程的最小元素,關(guān)鍵字、變量名、字面量、運(yùn)算符等都可以成為標(biāo)記,比如整型標(biāo)志int由三個(gè)字符構(gòu)成,但是它只是一個(gè)標(biāo)記,不可拆分。

語法分析是根據(jù)Token序列來構(gòu)造抽象語法樹的過程。抽象語法樹是一種用來描述程序代碼語法結(jié)構(gòu)的樹形表示方式,語法樹的每一個(gè)節(jié)點(diǎn)都代表著程序代碼中的一個(gè)語法結(jié)構(gòu),如bao、類型、修飾符、運(yùn)算符等。經(jīng)過這個(gè)步驟后,編譯器就基本不會(huì)再對(duì)源碼文件進(jìn)行操作了,后續(xù)的操作都建立在抽象語法樹之上。

填充符號(hào)表

完成了語法分析和詞法分析之后,下一步就是填充符號(hào)表的過程。符號(hào)表是由一組符號(hào)地址和符號(hào)信息構(gòu)成的表格。符號(hào)表中所登記的信息在編譯的不同階段都要用到,在語義分析(后面的步驟)中,符號(hào)表所登記的內(nèi)容將用于語義檢查和產(chǎn)生中間代碼,在目標(biāo)代碼生成階段,黨對(duì)符號(hào)名進(jìn)行地址分配時(shí),符號(hào)表是地址分配的依據(jù)。

語義分析

語法樹能表示一個(gè)結(jié)構(gòu)正確的源程序的抽象,但無法保證源程序是符合邏輯的。而語義分析的主要任務(wù)是讀結(jié)構(gòu)上正確的源程序進(jìn)行上下文有關(guān)性質(zhì)的審查。語義分析過程分為標(biāo)注檢查和數(shù)據(jù)及控制流分析兩個(gè)步驟:

標(biāo)注檢查步驟檢查的內(nèi)容包括諸如變量使用前是否已被聲明、變量和賦值之間的數(shù)據(jù)類型是否匹配等。

數(shù)據(jù)及控制流分析是對(duì)程序上下文邏輯更進(jìn)一步的驗(yàn)證,它可以檢查出諸如程序局部變量在使用前是否有賦值、方法的每條路徑是否都有返回值、是否所有的受查異常都被正確處理了等問題。

字節(jié)碼生成

字節(jié)碼生成是Javac編譯過程的最后一個(gè)階段。字節(jié)碼生成階段不僅僅是把前面各個(gè)步驟所生成的信息轉(zhuǎn)化成字節(jié)碼寫到磁盤中,編譯器還進(jìn)行了少量的代碼添加和轉(zhuǎn)換工作。?實(shí)例構(gòu)造器init()方法和類構(gòu)造器clinit()方法就是在這個(gè)階段添加到語法樹之中的(這里的實(shí)例構(gòu)造器并不是指默認(rèn)的構(gòu)造函數(shù),而是指我們自己重載的構(gòu)造函數(shù),如果用戶代碼中沒有提供任何構(gòu)造函數(shù),那編譯器會(huì)自動(dòng)添加一個(gè)沒有參數(shù)、訪問權(quán)限與當(dāng)前類一致的默認(rèn)構(gòu)造函數(shù),這個(gè)工作在填充符號(hào)表階段就已經(jīng)完成了)。

JIT編譯

Java程序最初是僅僅通過解釋器解釋執(zhí)行的,即對(duì)字節(jié)碼逐條解釋執(zhí)行,這種方式的執(zhí)行速度相對(duì)會(huì)比較慢,尤其當(dāng)某個(gè)方法或代碼塊運(yùn)行的特別頻繁時(shí),這種方式的執(zhí)行效率就顯得很低。于是后來在虛擬機(jī)中引入了JIT編譯器(即時(shí)編譯器),當(dāng)虛擬機(jī)發(fā)現(xiàn)某個(gè)方法或代碼塊運(yùn)行特別頻繁時(shí),就會(huì)把這些代碼認(rèn)定為“Hot?Spot?Code”(熱點(diǎn)代碼),為了提高熱點(diǎn)代碼的執(zhí)行效率,在運(yùn)行時(shí),虛擬機(jī)將會(huì)把這些代碼編譯成與本地平臺(tái)相關(guān)的機(jī)器碼,并進(jìn)行各層次的優(yōu)化,完成這項(xiàng)任務(wù)的正是JIT編譯器。

現(xiàn)在主流的商用虛擬機(jī)(如Sun?HotSpot、IBM?J9)中幾乎都同時(shí)包含解釋器和編譯器(三大商用虛擬機(jī)之一的JRockit是個(gè)例外,它內(nèi)部沒有解釋器,因此會(huì)有啟動(dòng)相應(yīng)時(shí)間長(zhǎng)之類的缺點(diǎn),但它主要是面向服務(wù)端的應(yīng)用,這類應(yīng)用一般不會(huì)重點(diǎn)關(guān)注啟動(dòng)時(shí)間)。二者各有優(yōu)勢(shì):當(dāng)程序需要迅速啟動(dòng)和執(zhí)行時(shí),解釋器可以首先發(fā)揮作用,省去編譯的時(shí)間,立即執(zhí)行;當(dāng)程序運(yùn)行后,隨著時(shí)間的推移,編譯器逐漸會(huì)返回作用,把越來越多的代碼編譯成本地代碼后,可以獲取更高的執(zhí)行效率。解釋執(zhí)行可以節(jié)約內(nèi)存,而編譯執(zhí)行可以提升效率。

HotSpot虛擬機(jī)中內(nèi)置了兩個(gè)JIT編譯器:Client?Complier和Server?Complier,分別用在客戶端和服務(wù)端,目前主流的HotSpot虛擬機(jī)中默認(rèn)是采用解釋器與其中一個(gè)編譯器直接配合的方式工作。

運(yùn)行過程中會(huì)被即時(shí)編譯器編譯的“熱點(diǎn)代碼”有兩類:

被多次調(diào)用的方法。

被多次調(diào)用的循環(huán)體。

兩種情況,編譯器都是以整個(gè)方法作為編譯對(duì)象,這種編譯也是虛擬機(jī)中標(biāo)準(zhǔn)的編譯方式。要知道一段代碼或方法是不是熱點(diǎn)代碼,是不是需要觸發(fā)即時(shí)編譯,需要進(jìn)行Hot?Spot?Detection(熱點(diǎn)探測(cè))。目前主要的熱點(diǎn)?判定方式有以下兩種:

基于采樣的熱點(diǎn)探測(cè):采用這種方法的虛擬機(jī)會(huì)周期性地檢查各個(gè)線程的棧頂,如果發(fā)現(xiàn)某些方法經(jīng)常出現(xiàn)在棧頂,那這段方法代碼就是“熱點(diǎn)代碼”。這種探測(cè)方法的好處是實(shí)現(xiàn)簡(jiǎn)單高效,還可以很容易地獲取方法調(diào)用關(guān)系,缺點(diǎn)是很難精確地確認(rèn)一個(gè)方法的熱度,容易因?yàn)槭艿骄€程阻塞或別的外界因素的影響而擾亂熱點(diǎn)探測(cè)。

基于計(jì)數(shù)器的熱點(diǎn)探測(cè):采用這種方法的虛擬機(jī)會(huì)為每個(gè)方法,甚至是代碼塊建立計(jì)數(shù)器,統(tǒng)計(jì)方法的執(zhí)行次數(shù),如果執(zhí)行次數(shù)超過一定的閥值,就認(rèn)為它是“熱點(diǎn)方法”。這種統(tǒng)計(jì)方法實(shí)現(xiàn)復(fù)雜一些,需要為每個(gè)方法建立并維護(hù)計(jì)數(shù)器,而且不能直接獲取到方法的調(diào)用關(guān)系,但是它的統(tǒng)計(jì)結(jié)果相對(duì)更加精確嚴(yán)謹(jǐn)。

在HotSpot虛擬機(jī)中使用的是第二種——基于計(jì)數(shù)器的熱點(diǎn)探測(cè)方法,因此它為每個(gè)方法準(zhǔn)備了兩個(gè)計(jì)數(shù)器:方法調(diào)用計(jì)數(shù)器和回邊計(jì)數(shù)器。

方法調(diào)用計(jì)數(shù)器用來統(tǒng)計(jì)方法調(diào)用的次數(shù),在默認(rèn)設(shè)置下,方法調(diào)用計(jì)數(shù)器統(tǒng)計(jì)的并不是方法被調(diào)用的絕對(duì)次數(shù),而是一個(gè)相對(duì)的執(zhí)行頻率,即一段時(shí)間內(nèi)方法被調(diào)用的次數(shù)。

回邊計(jì)數(shù)器用于統(tǒng)計(jì)一個(gè)方法中循環(huán)體代碼執(zhí)行的次數(shù)(準(zhǔn)確地說,應(yīng)該是回邊的次數(shù),因?yàn)椴⒎撬械难h(huán)都是回邊),在字節(jié)碼中遇到控制流向后跳轉(zhuǎn)的指令就稱為“回邊”。

在確定虛擬機(jī)運(yùn)行參數(shù)的前提下,這兩個(gè)計(jì)數(shù)器都有一個(gè)確定的閥值,當(dāng)計(jì)數(shù)器的值超過了閥值,就會(huì)觸發(fā)JIT編譯。觸發(fā)了JIT編譯后,在默認(rèn)設(shè)置下,執(zhí)行引擎并不會(huì)同步等待編譯請(qǐng)求完成,而是繼續(xù)進(jìn)入解釋器按照解釋方式執(zhí)行字節(jié)碼,直到提交的請(qǐng)求被編譯器編譯完成為止(編譯工作在后臺(tái)線程中進(jìn)行)。當(dāng)編譯工作完成后,下一次調(diào)用該方法或代碼時(shí),就會(huì)使用已編譯的版本。

由于方法計(jì)數(shù)器觸發(fā)即時(shí)編譯的過程與回邊計(jì)數(shù)器觸發(fā)即時(shí)編譯的過程類似,因此這里僅給出方法調(diào)用計(jì)數(shù)器觸發(fā)即時(shí)編譯的流程:


網(wǎng)站欄目:java虛擬機(jī)代碼塊 Java虛擬機(jī)源碼
標(biāo)題路徑:http://weahome.cn/article/hgehds.html

其他資訊