成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

JAVA 虛擬機(jī)類加載機(jī)制和字節(jié)碼執(zhí)行引擎

RichardXG / 2360人閱讀

摘要:實(shí)現(xiàn)這個(gè)口號(hào)的就是可以運(yùn)行在不同平臺(tái)上的虛擬機(jī)和與平臺(tái)無關(guān)的字節(jié)碼。類加載過程加載加載是類加載的第一個(gè)階段,虛擬機(jī)要完成以下三個(gè)過程通過類的全限定名獲取定義此類的二進(jìn)制字節(jié)流。驗(yàn)證目的是確保文件字節(jié)流信息符合虛擬機(jī)的要求。

引言

我們知道java代碼編譯后生成的是字節(jié)碼,那虛擬機(jī)是如何加載這些class字節(jié)碼文件的呢?加載之后又是如何進(jìn)行方法調(diào)用的呢?

一 類文件結(jié)構(gòu) 無關(guān)性基石

java有一個(gè)口號(hào)叫做一次編寫,到處運(yùn)行。實(shí)現(xiàn)這個(gè)口號(hào)的就是可以運(yùn)行在不同平臺(tái)上的虛擬機(jī)和與平臺(tái)無關(guān)的字節(jié)碼。這里要注意的是,虛擬機(jī)也是中立的,只要是符合規(guī)范的字節(jié)碼,都可以被虛擬機(jī)接受,例如Groovy,JRuby等語言,都會(huì)生成符合規(guī)范的字節(jié)碼,然后被虛擬機(jī)所運(yùn)行,虛擬機(jī)不關(guān)心字節(jié)碼由哪種語言生成。

類文件結(jié)構(gòu)

class類文件是一組以8位字節(jié)為基礎(chǔ)的二進(jìn)制流,它包含以下幾個(gè)部分:

魔數(shù)和class文件版本:類文件開頭的四個(gè)字節(jié)被定義為CAFEBABE,只有開頭為CAFEBABE的文件才可以被虛擬機(jī)接受,接下來四個(gè)字節(jié)為class文件的版本號(hào),高版本JDK可以兼容以前版本的class文件,但不能運(yùn)行以后版本的class文件。
常量池:可以理解為class文件中的資源倉庫,它包含兩大類常量:字面量和符號(hào)引用,字面量包含文本字符串,聲明為final的常量值等,符號(hào)引用包含類和接口的全限定名,字段的名稱和描述符,方法的名稱和描述符。

訪問標(biāo)志:常量池結(jié)束后,緊接著兩個(gè)字節(jié)表示訪問標(biāo)志,用于識(shí)別一些類或接口層次的訪問信息,例如是否是public,是否是static等。
類索引,父類索引,和接口索引集合:類索引用來確定這個(gè)類的全限定名,父類為父類的全限定名,接口索引集合為接口的全限定名。
字段表集合:用于描述接口或者類中聲明的變量,但不包含方法中的變量。
方法表集合:用于表述接口或者類中的方法。
屬性表集合:class文件,字段表,方法表中的屬性都源自這里。

二 類加載機(jī)制
虛擬機(jī)把描述類的數(shù)據(jù)從class文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換分析和初始化,最終形成可以被虛擬節(jié)直接使用的JAVA類型,這就是虛擬機(jī)的類加載機(jī)制。

類從被加載到虛擬機(jī)內(nèi)存到卸載出內(nèi)存的生命周期包括:加載->連接(驗(yàn)證->準(zhǔn)備->解析)->初始化->使用->卸載。

初始化的5種情況:

使用new關(guān)鍵字實(shí)例化對(duì)象時(shí),讀取或設(shè)置一個(gè)類的靜態(tài)字段,除被final修飾經(jīng)編譯結(jié)果放在常量池的靜態(tài)字段,調(diào)用類的靜態(tài)方法時(shí)。

使用java.lang.reflect包方法對(duì)類進(jìn)行反射調(diào)用時(shí)。(Class.forName())。

初始化子類時(shí),如果父類沒有初始化。

虛擬機(jī)啟動(dòng)時(shí)main方法所在的類。

當(dāng)使用JDK1.7動(dòng)態(tài)語言支持時(shí),java.lang.invoke.MethodHandle實(shí)例解析結(jié)果為REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,且對(duì)應(yīng)類沒有進(jìn)行初始化。

類加載過程

加載

加載是類加載的第一個(gè)階段,虛擬機(jī)要完成以下三個(gè)過程:

通過類的全限定名獲取定義此類的二進(jìn)制字節(jié)流。

將字節(jié)流的存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)結(jié)構(gòu)。

在內(nèi)存中生成一個(gè)代表該類的Class對(duì)象,作為方法區(qū)各種數(shù)據(jù)的訪問入口。

驗(yàn)證

目的是確保class文件字節(jié)流信息符合虛擬機(jī)的要求。

準(zhǔn)備

為static修飾的變量賦初值,例如int型默認(rèn)為0,boolean默認(rèn)為false。

解析

虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換成直接引用。

初始化

初始化是類加載的最后一個(gè)階段,將執(zhí)行類構(gòu)造器< init>()方法,注意這里的方法不是構(gòu)造方法。該方法將會(huì)顯式調(diào)用父類構(gòu)造器,接下來按照java語句順序?yàn)轭愖兞亢挽o態(tài)語句塊賦值。

類加載器

對(duì)于任意一個(gè)類,都需要由加載它的類加載器和這個(gè)類本身一同確立其在java虛擬機(jī)中的唯一性。舉一個(gè)例子:

package com.sinaapp.gavinzhang.bean;
import java.io.InputStream;
 
public class App 
{
    public static void main( String[] args )
    {
        ClassLoader myClassLoader = new ClassLoader() {
            @Override
            public Class loadClass(String name) throws ClassNotFoundException {
                try{
                    String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
                    InputStream is = getClass().getResourceAsStream(fileName);
                    if(is == null)
                    {
                        System.out.println(fileName+ "is not find");
                        return super.loadClass(name);
                    }
                    System.out.println("fileName: "+fileName);
                    byte[] b = new byte[is.available()];
                    is.read(b);
                    return defineClass(name,b,0,b.length);
                }catch (Exception E)
                {
                    throw new ClassCastException(name);
                }
 
            }
        };
        try {
            Object obj = myClassLoader.loadClass("com.sinaapp.gavinzhang.bean.Resource").newInstance();
            Object obj1  = Class.forName("com.sinaapp.gavinzhang.bean.Resource").newInstance();
            System.out.println(obj instanceof com.sinaapp.gavinzhang.wesound.bean.Resource);
            System.out.println(obj1 instanceof com.sinaapp.gavinzhang.wesound.bean.Resource);
        }catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

結(jié)果為:

可以看到,由自定義的加載類只能獲取同包下的class,而系統(tǒng)的class不能被加載,而且由Class.forName()獲取的類與自定義加載類得到的類不是同一個(gè)類。

根據(jù)五種初始化的條件,父類也會(huì)被初始化,但是,上邊的代碼運(yùn)行結(jié)果顯示,父類和接口都沒有被初始化,這又是怎么回事呢?

系統(tǒng)提供了三種類加載器,分別是:

啟動(dòng)類加載器(Bootstrap ClassLoader),該加載器會(huì)將lib目錄下能被虛擬機(jī)識(shí)別的類加載到內(nèi)存中。

擴(kuò)展類加載器(Extension ClassLoader),該加載器會(huì)將libext目錄下的類庫加載到內(nèi)存。

應(yīng)用程序類加載器(Application ClassLoader),該加載器負(fù)責(zé)加載用戶路徑上所指定的類庫。

我們自定義的ClassLoader繼承自應(yīng)用程序類加載器,當(dāng)自定義類加載器找不到所加在的類時(shí),會(huì)使用啟動(dòng)類加載器進(jìn)行加載,當(dāng)啟動(dòng)類加載器加載不到時(shí),由擴(kuò)展類加載,擴(kuò)展類加載不到時(shí)有應(yīng)用程序類加載。這也是為什么上邊的代碼能夠成功運(yùn)行的原因。

三 字節(jié)碼執(zhí)行引擎 運(yùn)行時(shí)棧幀結(jié)構(gòu)

http://segmentfault.com/a/119... 中講到虛擬機(jī)棧是線程私有的,線程中會(huì)為運(yùn)行的方法創(chuàng)建棧幀。

棧幀是虛擬機(jī)棧的棧元素,棧幀存儲(chǔ)了局部變量表,操作數(shù)棧,動(dòng)態(tài)連接,返回地址等信息。每一個(gè)方法的調(diào)用都對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中的入棧和出棧。

局部變量表由方法參數(shù),方法內(nèi)定義的局部變量組成,容量以變量槽(Slot)為最小單位。如果該方法不是static方法,則局部變量表的第一個(gè)索引為該對(duì)象的引用,用this可以取到。

操作數(shù)棧最開始為空,由字節(jié)碼指令往棧中存數(shù)據(jù)和取數(shù)據(jù),方法的返回值也會(huì)存到上一個(gè)方法的操作數(shù)棧中。

動(dòng)態(tài)連接含有一個(gè)指向常量池中該棧幀所屬方法的引用,持有該引用是為了進(jìn)行動(dòng)態(tài)分派。

方法返回地址存放的是調(diào)用該方法的pc計(jì)數(shù)器值,當(dāng)方法正常返回時(shí),就會(huì)把返回值傳遞到上層方法調(diào)用者。當(dāng)方法中發(fā)生沒有可被捕獲的異常,也會(huì)返回,但是不會(huì)向上層傳遞返回值。

方法調(diào)用

java是一門面向?qū)ο蟮恼Z言,它具有多態(tài)性。那么虛擬機(jī)又是如何知道運(yùn)行時(shí)該調(diào)用哪一個(gè)方法?

靜態(tài)分派是在編譯期就決定了該調(diào)用哪一個(gè)方法而不是由虛擬機(jī)來確定,方法重載就是典型的靜態(tài)分派。

動(dòng)態(tài)分派是在虛擬機(jī)運(yùn)行階段才能決定調(diào)用哪一個(gè)方法,方法重寫就是典型的動(dòng)態(tài)分派。

動(dòng)態(tài)分派的實(shí)現(xiàn):當(dāng)調(diào)用一個(gè)對(duì)象的方法時(shí),會(huì)將該對(duì)象的引用壓棧到操作數(shù)棧,然后字節(jié)碼指令invokevirtual會(huì)去尋找該引用實(shí)際類型。如果在實(shí)際類型中找對(duì)應(yīng)的方法,且訪問權(quán)限足夠,則直接返回該方法引用,否則會(huì)依照繼承關(guān)系對(duì)父類進(jìn)行查找。實(shí)際上,如果子類沒有重寫父類方法,則子類方法的引用會(huì)直接指向父類方法。

基于棧的字節(jié)碼執(zhí)行引擎

不管是解釋型語言還是編譯型語言,機(jī)器都無法理解非二進(jìn)制語言。高級(jí)語言轉(zhuǎn)化成機(jī)器語言都遵循現(xiàn)代經(jīng)典編譯原理。即執(zhí)行前對(duì)程序源碼進(jìn)行詞法和語法分析,構(gòu)建抽象語法樹。C語言等編譯型語言會(huì)由多帶帶的執(zhí)行引擎做這些工作,而Java語言等解釋型語言語法抽象樹由jvm完成。jvm可以選擇通過解釋器來解釋字節(jié)碼執(zhí)行還是通過優(yōu)化器生成機(jī)器代碼來執(zhí)行。

常用的兩套指令集架構(gòu)分別是基于棧的指令集和基于寄存器的指令集。

基于棧的指令集更多的通過入棧出棧來實(shí)現(xiàn)計(jì)算功能,例如1+1

    iconst_1  ;將1入棧
    iconst_1  ;將1入棧
    iadd      ;將棧頂兩個(gè)元素取出相加并將結(jié)果入棧

基于寄存器的指令集更多的是使用寄存器來進(jìn)行操作,例如1+1

mov eax,1 ;向eax中存1
add eax,1 ;eax<-eax+1

總體來說,基于棧的指令集會(huì)慢一些,但是它與寄存器無關(guān),更容易實(shí)現(xiàn)到處運(yùn)行的目標(biāo)。

總結(jié)

又到了該總結(jié)的時(shí)候了,類加載機(jī)制面試中很容易被問到,不幸的是,當(dāng)時(shí)我并沒有看這方面的知識(shí)。

class類文件結(jié)構(gòu)的每一個(gè)部分都可以再深入下去,類文件結(jié)構(gòu)是采用結(jié)構(gòu)體的方式存儲(chǔ)的,那么怎么知道集合的長(zhǎng)度,各個(gè)屬性又是怎么被標(biāo)記的。

類加載機(jī)制中有且僅有的五種觸發(fā)初始化的情況。類加載器的分類。

棧幀的結(jié)構(gòu),以及方法調(diào)用。

java語言的方法調(diào)用分為靜態(tài)多分派,動(dòng)態(tài)單分派。

更多文章:http://blog.gavinzh.com

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64402.html

相關(guān)文章

  • 深入理解虛擬機(jī)之虛擬機(jī)類加載機(jī)制

    摘要:最終形成可以被虛擬機(jī)最直接使用的類型的過程就是虛擬機(jī)的類加載機(jī)制。即重寫一個(gè)類加載器的方法驗(yàn)證驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。 《深入理解Java虛擬機(jī):JVM高級(jí)特性與最佳實(shí)踐(第二版》讀書筆記與常見相關(guān)面試題總結(jié) 本節(jié)常見面試題(推薦帶著問題閱讀,問題答案在文中都有提到): 簡(jiǎn)單說說類加載過...

    MadPecker 評(píng)論0 收藏0
  • 虛擬機(jī)類加載機(jī)制

    摘要:加載階段在類的加載階段,虛擬機(jī)需要完成以下件事情通過一個(gè)類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流。驗(yàn)證階段驗(yàn)證是連接階段的第一步,這一階段的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。 注:本篇文章中的內(nèi)容是根據(jù)《深入理解Java虛擬機(jī)--JVM高級(jí)特性與最佳實(shí)踐》而總結(jié)的,如有理解錯(cuò)誤,歡迎大家指正! 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件...

    k00baa 評(píng)論0 收藏0
  • Java 虛擬機(jī)總結(jié)給面試的你(中)

    摘要:驗(yàn)證過程驗(yàn)證過程的目的是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)自身的安全。二虛擬機(jī)字節(jié)碼執(zhí)行引擎虛擬機(jī)的執(zhí)行引擎自行實(shí)現(xiàn),可以自行制定指令集與執(zhí)行引擎的結(jié)構(gòu)體系。 本篇博客主要針對(duì)Java虛擬機(jī)的類加載機(jī)制,虛擬機(jī)字節(jié)碼執(zhí)行引擎,早期編譯優(yōu)化進(jìn)行總結(jié),其余部分總結(jié)請(qǐng)點(diǎn)擊Java虛擬總結(jié)上篇 。 一.虛擬機(jī)類加載機(jī)制 概述 虛擬機(jī)把描述類的數(shù)據(jù)從Clas...

    MRZYD 評(píng)論0 收藏0
  • Java虛擬機(jī)類加載過程

    摘要:二驗(yàn)證驗(yàn)證主要是為了確保文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會(huì)危害虛擬機(jī)的自身安全。五初始化類的初始化階段是類加載過程的最后一步,該階段才真正開始執(zhí)行類中定義的程序代碼或者說是字節(jié)碼。 關(guān)注我,每天三分鐘,帶你輕松掌握一個(gè)Java相關(guān)知識(shí)點(diǎn)。 虛擬機(jī)(JVM)經(jīng)常出現(xiàn)在我們面試中,但是工作中卻很少遇到,導(dǎo)致很多同學(xué)沒有去了解過。其實(shí)除了應(yīng)付面試,作為java程序員,了解...

    lentoo 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<