多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內(nèi)最全I(xiàn)T社區(qū)平臺 聯(lián)系我們 | 收藏本站
阿里云優(yōu)惠2
您當(dāng)前位置:首頁 > php開源 > php教程 > 《深入理解Java虛擬機(jī)》筆記

《深入理解Java虛擬機(jī)》筆記

來源:程序員人生   發(fā)布時間:2017-02-08 08:29:22 閱讀次數(shù):3325次

學(xué)習(xí)Java的同學(xué)注意了!!! 
學(xué)習(xí)進(jìn)程中遇到甚么問題或想獲得學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交換群,群號碼:183993990  我們1起學(xué)Java!


在C里面我們想履行1段自己編寫的機(jī)器指令的方法大概以下:

typedef void(*FUNC)(int);
char* str = "your code";
FUNC f = (FUNC)str;
(*f)(0);

  也就是說,我們完全可以做1個工具,從1個文件中讀入指令,然后將這些指令運(yùn)行起來。上面代碼中“編好的機(jī)器指令”固然指的是能在CPU上運(yùn)行的,如果這里我還實(shí)現(xiàn)了1個翻譯機(jī)器:從自己定義的格式指令翻譯到CPU指令,那末就能夠履行根據(jù)自定義格式的代碼了。那末上面這段代碼是否是相當(dāng)于最簡單的1個虛擬機(jī)了?下面來看JVM的整體結(jié)構(gòu):


ClassLoader的作用是裝載能被JVM辨認(rèn)的指令(固然不只是從磁盤文件或內(nèi)存去裝載),那末我們先了解1下該格式:

魔數(shù)和版本就不說了(滿大街的文件格式都是這個東西),接著的便是常量池,其中不過是兩種東西:

  1. 字面常量(比如Integer、Long、String等);
  2. 符號援用(方法是哪里的?甚么樣的?);

而我們知道,在JVM里面Class都是根據(jù)全限定名去找的,那末方法的描寫固然也應(yīng)當(dāng)如此,那末就得到這些常量之間的關(guān)系以下:

 

在接下來的“訪問權(quán)限”中表明了該Class是public還是private等,而this&super&interface則表面了“本類”、“繼承自哪一個類”、“實(shí)現(xiàn)了哪些接口”,實(shí)際上這里只是保存了代表這些信息的CONSTANT_Class_info的下標(biāo)(u2)。

 

感覺這里的NameIndex和DescriptorIndex加起來和NameAndType有點(diǎn)像,那末為何不直接用1個NameAndType的索引值表示?MethodInfo和FieldInfo之間最大的不同點(diǎn)就是Attributes。比如FieldInfo的屬性表中寄存的是變量的初始值,而MethodInfo的屬性表中寄存的則是字節(jié)碼。那末我們來順次看這些Attributes,首先是Code:

 

有幾個成心思的地方:

  1. 從Class文件中可以知道在履行的進(jìn)程中棧的深度;
  2. 對非靜態(tài)方法,編譯器會將this通過參數(shù)傳遞給方法;
  3. 異常表中記錄的范圍是指令的行數(shù)(而不是源代碼的);
  4. 這里的異常是指try-catch中的,而與Code同級的異常表中的則是指throws出去的;

Exceptions則非常簡單:

LineNumberTable保存了字節(jié)碼和源碼之間的關(guān)系,結(jié)構(gòu)以下:

 

LocalVariableTable描寫了棧幀中局部變量表的變量和源代碼中定義的變量之間的關(guān)系,結(jié)構(gòu)以下:

 

SourceFile指明了生成該Class文件的Java源碼文件名(比如在1個Java文件中申明了很多類的時候會生成很多Class文件),結(jié)構(gòu)以下:

Deprecated和Synthetic屬性只存在“有”和“沒有”的區(qū)分:

  1. Deprecated:被程序作者定為不再推薦使用,通過@deprecated注釋說明;
  2. Synthetic:表示字段或方法是由編譯器自動生成的,比如<init>; 

這也就是為何Code屬性后面會有Attribute的緣由?

類加載的時機(jī)就很簡單了:在用到的時候就加載(空話!)。下來看1下類加載的進(jìn)程:

 

履行上面這段進(jìn)程的是:ClassLoader,這個東西還是非常重要的,在JVM中是通過ClassLoader和類本身共同去判斷兩個Class是不是相同。換句話說就是:不同的ClassLoader加載同1個Class文件,那末JVM認(rèn)為他們生成的類是不同的。有些時候不會從Class文件中加載流(比如Java Applet是從網(wǎng)絡(luò)中加載),那末這個ClassLoader和普通的實(shí)現(xiàn)邏輯固然是不1樣的,通過不同的ClassLoader就能夠解決這個問題。

但是允許使用不同的ClassLoader又引發(fā)了新的問題:如果我也聲明了1個java.lang.Integer,但是里面的代碼非常危險(xiǎn),怎樣辦?這里就引出了雙親委派模式:

除頂層的啟動類加載器外,其余的類加載器都應(yīng)當(dāng)有父類加載器(通過組合實(shí)現(xiàn)),它在接到加載類的要求時優(yōu)先委派給父類加載器去完成。

這樣的話,在加載java.lang.Integer的時候會優(yōu)先使用系統(tǒng)的類加載器,這樣就不會加載用戶自己寫的。在Java程序員看到有3種系統(tǒng)提供的類加載器:

  1. Bootstrap ClassLoader:負(fù)責(zé)加載<JAVA_HOME>\lib目錄中的類庫,沒法被Java程序直接援用;
  2. Extension ClassLoader:負(fù)責(zé)加載<JAVA_HOME>\lib\ext,開發(fā)者可以直接使用;
  3. Application ClassLoader:加載ClassPath上所指定的類庫,如果沒有自己定義過自己的類加載器則會使用它;

這樣默許的類會是有Application ClassLoader去加載類,然后如果發(fā)現(xiàn)要使用新的類型的時候則會遞歸地使用Application ClassLoader去加載(在前面的加載進(jìn)程中提到)。這樣,只有在自己的程序中能使用自己編寫的ClassLoader去加載類,并且這個被加載的類是不能被他人使用的。

雙親委派模式不是1個強(qiáng)迫性的束縛,而是Java設(shè)計(jì)者推薦給開發(fā)者的類加載實(shí)現(xiàn)方式。雙親委派模式出現(xiàn)過的3次“破壞”:

  1. 為了兼容JDK 1.0,建議使用者去覆蓋findClass方法;
  2. 在基礎(chǔ)類要訪問用戶類的代碼會出現(xiàn)問題(比如JNDI):線程山下文類加載器;
  3. 用戶的1些需求,比如HotSwap、OSGI等; 

加載完完成后,接下來就要看程序是怎樣運(yùn)行的。棧幀是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和履行,幀的意思就是1個單位,在調(diào)用其他方法的時候會向棧中壓入棧幀,結(jié)構(gòu)以下:

 

在Class文件編譯完成以后,在運(yùn)行的時候需要多少個局部變量就已肯定(在前面Class文件中也已看到過了),那末這里需要注意這個特性可能會引發(fā)GC(具體如何引發(fā)就不在這里細(xì)說了)。在棧中,總是底層的棧去調(diào)用高層的棧(并且1定的相鄰的),那末他們在參數(shù)傳遞(返回結(jié)果)的時常常是通過將其壓入操作數(shù)棧,有些虛擬機(jī)為了提高這部份的效力使得相鄰棧幀“糾纏”在1起:

 

那末我們接下來要去看是方法是如何履行的,第1個問題就是履行哪一個方法?在“面向進(jìn)程”的編程中似乎不存在在個問題,但是在Java OR C++中這都是比較蛋疼的1個問題。緣由就是平時不會這么用,但是你必須去弄明白= =。JVM肯定目標(biāo)方法的時候有兩種方法:

  1. 靜態(tài)分派:根據(jù)參數(shù)類型和方法名稱來決定調(diào)用哪一個方法。但是,其實(shí)不是說沒有發(fā)現(xiàn)匹配的類型就報(bào)錯,比如有:func(int a),而在調(diào)用func('a')的時候也會調(diào)用該方法(固然是在沒有func(char a)的條件下),這樣給人的關(guān)鍵就有點(diǎn)像1個處理的鏈條。不管多么復(fù)雜,這些都是在編譯期間肯定的,由于這里是向上找的。
  2. 動態(tài)分派:最普遍的就是Interface a = new Implements(),a調(diào)用方法到底應(yīng)當(dāng)是哪一個類的在編譯期間是沒法肯定的。其實(shí)動態(tài)分派實(shí)現(xiàn)起來也很簡單:在調(diào)用方法的時候先拿到對象的實(shí)際類型。

其實(shí)“靜態(tài)”和“動態(tài)”給人的感覺還是比較模糊的,“靜態(tài)分派”給人的感覺是根據(jù)參數(shù)的類型向上查找方法,“動態(tài)分派”給人的感覺則是根據(jù)實(shí)例的真實(shí)類型向上查找。虛擬機(jī)優(yōu)化動態(tài)分派的效力1般是為類在方法區(qū)中建立1個虛方法表:

虛方法表中寄存各個方法實(shí)際入口地址,如果某個方法在子類中沒有被重寫,那末子類的虛方法表里面的地址入口和父類相同方法的地址入口是1致的,都指向父類的實(shí)現(xiàn)入口。如果子類重寫了這個方法,子類方法表中的地址將會被替換為指向子類實(shí)現(xiàn)版本的入口地址。其實(shí)往簡單里說,就是1個預(yù)處理。

具體單個方法的履行非常簡單,寫1個簡單的程序然后使用javap -c,再結(jié)合每條指令的含義就可以大概知道程序時怎樣履行和返回的了(大體上就是基于棧),這里就不深入和細(xì)說了。

1般情況下,從Java文件到運(yùn)行起來,總的會經(jīng)歷兩個階段:Java到Class文件和履行Class文件。第1個階段其實(shí)就是編譯了,在這個進(jìn)程中比較成心思的是“語法糖”(其他的比如詞法分析和語法分析就不說了,此處省略1萬字~!~)。所謂Java的語法糖是:for遍歷的簡寫、自動裝箱、泛型等(其實(shí)有無感覺String+String也是語法糖,在實(shí)際中會變成StringBuffer的append)。其中比較成心思的是泛型:

Java中的泛型和C++中的泛型原理是上不1樣的:對C++來講List<A>和List<B>就是兩個東西,而在Java中List<A>和List<B>都是List<Object>,由于在Java中Object是所有對象的父對象,那末Object o可以指向所有的對象,那末就能夠用List<Object>來保存所有的對象集了(感覺實(shí)現(xiàn)的有點(diǎn)廢)。

這里觸及到1個問題就是對象刪除,比以下面代碼:

static void func(List<Integer> a){
        return;
}

在使用javap查看生成的Class的時候會發(fā)現(xiàn):

static void func(java.util.List);
  Signature: (Ljava/util/List;)V
  Code:
   0:   return

其中根本沒有任何Integer的痕跡,但是如果加上返回值,也就是:

static Integer func(List<Integer> a){
        return null;
}

此時再查看的時候就會變成:
static java.lang.Integer func(java.util.List);
  Signature: (Ljava/util/List;)Ljava/lang/Integer;
  Code:
   0:   aconst_null
   1:   areturn
通過泛型實(shí)現(xiàn)的原理可以理解很多在實(shí)際中會遇到的問題,比如使用List的時候稀里糊涂的類型強(qiáng)迫轉(zhuǎn)換毛病。

接下來開始討論第2個部份,也就是Class文件的實(shí)際的履行。在C++中常會提到的兩個概念是:Debug和Release,而在Java中常提到的兩個概念是Server和Client(雖然他們劃分的根據(jù)完全不1樣),Client和Server兩種模式對應(yīng)兩種編譯器:

  1. Client對應(yīng)C1編譯:將字節(jié)碼編譯本錢地代碼并進(jìn)行耗時短且可靠的優(yōu)化,在必要的時候加入性能監(jiān)控。
  2. Server對應(yīng)C2編譯:將字節(jié)碼編譯本錢地代碼并進(jìn)行耗時比較長的優(yōu)化,還可能會根據(jù)性能監(jiān)控的結(jié)果進(jìn)行1些不可靠激進(jìn)的優(yōu)化。

在監(jiān)測器發(fā)現(xiàn)有熱門代碼(被調(diào)用了很屢次的方法或是履行很屢次的循環(huán)體)的情況下,將會想即時編譯器提交1個該該方法的代碼編譯要求。當(dāng)這個方法再次被調(diào)用時,會先檢查改方法是不是存在被JIT編譯過的版本,如果存在則優(yōu)先使用編譯后的本地代碼。在默許的情況下,編譯本地代碼的進(jìn)程和舊的代碼(也就是解釋履行字節(jié)碼)是并行的,可使用-XX:-BackgroundCompilation來制止后臺編譯,也就是說履行線程會登島編譯完成后再去履行生成的本地代碼。

在具體編譯優(yōu)化的時候有1個比較好玩的東西,逃逸分析(所謂逃逸是指能被從方法外援用),對不會逃逸的對象可以進(jìn)行優(yōu)化:

  1. 在棧上分配對象,可以減少GC的壓力;
  2. 不需要對為逃逸的對象進(jìn)行線程同步;
  3. 如果1個對象沒法逃逸,可以在方法里面不申明這個對象,而是放1些“零件”;

關(guān)于Java和C++效力的問題,感覺討論起來就沒有甚么意義了:語言到最后肯定是要生成機(jī)器指令的,在語言的機(jī)制上面各有千秋,致使不同的語言之間生成機(jī)器指令的進(jìn)程可能不同,但是這個生成的進(jìn)程跟我們這些碼農(nóng)沒有半毛錢關(guān)系(更準(zhǔn)確的說我們生成的進(jìn)程我們毛都不知道),所以在弄清楚之前就不要爭到底哪一個效力高(乃至是哪一個更好)。 

程序的并發(fā)主要是斟酌不同的線程操作同1塊內(nèi)存時候可能產(chǎn)生的1些問題(至于文件鎖之類的東西,咳咳),首先就先了解線程和內(nèi)存的關(guān)系:

 

這里的主內(nèi)存就像是內(nèi)存條,工作內(nèi)存就像是寄存器+Cache。Java內(nèi)存模型定義了8中操作,他們的履行以下:

 

Java虛擬機(jī)中最輕量級的同步機(jī)制:volatile,它的性質(zhì)以下:

  1. 變量產(chǎn)生修改的時候會立刻被其他線程看到;
  2. 制止指令重排序優(yōu)化;

從Java內(nèi)存模型操作的角度來看volatile的實(shí)現(xiàn)還是挺簡單的:在use之前必須load,在assign以后必須store,這樣就保證了每次用都是從主內(nèi)存中讀取,每次賦值以后都會同步到主內(nèi)存(貌似說的是空話)。線程的同步主要是從3個方面斟酌:

  1. 原子性:Long和Double需要特殊斟酌;
  2. 可見性;除volatile以外還有final(synchronized就不說了吧);
  3. 有序性:指令重排,固然可以制止指令重排;

如果任什么時候候都斟酌同步那代碼寫起來就累死了。下面是Java內(nèi)存模型的天然先行產(chǎn)生關(guān)系:

  1. 控制流被履行的順序和代碼的順序保持1致;
  2. unlock先行產(chǎn)生于后面對同1個鎖的lock操作;
  3. 對volatile變量的寫操作先行于后面對這個變量的讀操作;
  4. Thread的start方法先行于線程的任何1個動作;
  5. 線程的所有動作都先行于線程的終止檢測;
  6. 對線程interrupt方法的調(diào)用先行于被中斷線程的代碼檢測到的中斷事件的產(chǎn)生;
  7. 對象的初始化完成先行于finalize方法調(diào)用;
  8. 傳遞性;

其實(shí)上面的這8條規(guī)則還是很成心思的,如果其中的某1條不成立會產(chǎn)生甚么?說到底Java線程還是用戶級的線程,那末它究竟是個甚么東西(在學(xué)C的時候也糾結(jié)過這個問題- -)。實(shí)現(xiàn)線程主要有幾種方式:

  1. 使用1個內(nèi)核線程(輕量級進(jìn)程)來代理;
  2. 完全在用戶態(tài)實(shí)現(xiàn),內(nèi)核都感覺不到;
  3. 用戶和內(nèi)核混合實(shí)現(xiàn),各自做自己善于的事情;

這里就不深入的去看了(雖然這里的介紹根沒說1樣),想一想看都知道不同虛擬機(jī)在不同的操作系統(tǒng)上面的實(shí)現(xiàn)方式極可能是不1樣,如果想深入看還是pthread比較成心思1點(diǎn)。關(guān)于線程的其他要注意的地方(比如狀態(tài)轉(zhuǎn)移甚么的)就不在這里討論了。

線程安全:當(dāng)多個線程訪問1個對象時,如果不用斟酌這些線程在運(yùn)行時環(huán)境下的調(diào)度和交替履行,也不需要進(jìn)行額外的同步,或在調(diào)用方進(jìn)行任何其他的調(diào)和操作,調(diào)用這個對象的行動都可以取得正確的結(jié)果,那這個對象是線程安全的。

Java中線程同享的變量可以分為以下5種:

  1. 不可變:這個就不需要解釋了(其實(shí)不1定非得用final修飾);
  2. 絕對線程安全:也就是滿足上面的線程安全描寫的;
  3. 相對線程安全:簡單的說應(yīng)當(dāng)是對單個行動的調(diào)用不會出錯;
  4. 線程兼容:對象其實(shí)不是線程安全,但可以通過調(diào)用方的同步來彌補(bǔ);
  5. 線程對峙:不管調(diào)用方怎樣處理都不能在多線程環(huán)境下使用;

鎖的話有以下幾種實(shí)現(xiàn)方式:

  1. 互斥同步,其實(shí)不是說等待的線程會1直等下去;
  2. 非阻塞同步,樂觀(沖突并沒有我們想象的那末多);

如果線程之間的切換非常頻繁的話自旋鎖是1個不錯的選擇,這樣就不需要線程切換時候的系統(tǒng)調(diào)用的開消了。如果1個任務(wù)能夠很快的完成的話,將全部進(jìn)程都鎖住也許是個不錯的選擇(而不是給每一個子進(jìn)程上鎖)。其他的鎖優(yōu)化包括“輕量級鎖”和“偏心鎖”。

學(xué)習(xí)Java的同學(xué)注意了!!! 
學(xué)習(xí)進(jìn)程中遇到甚么問題或想獲得學(xué)習(xí)資源的話,歡迎加入Java學(xué)習(xí)交換群,群號碼:183993990  我們1起學(xué)Java!

生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學(xué)習(xí)有所幫助,可以手機(jī)掃描二維碼進(jìn)行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關(guān)閉
程序員人生
主站蜘蛛池模板: 久久精品免费观看 | 最近在线更新中文字幕1 | 99热久久最新地址获6取 | 亚洲一区二区三区视频 | 精品女人 | 武则天全黄肉体毛片免费看 | 国产婷婷综合在线视频 | 国产三级第一页 | 国产精品久久现线拍久青草 | 中文字幕免费在线 | 伊人不卡久久大香线蕉综合影院 | 亚洲自拍偷拍专区 | 在线亚洲欧洲福利视频 | 一级特黄aa大片欧美网站 | 超高清欧美同性videos | 永久精品| 五月婷婷在线观看 | 玖操网| 国产精品自在线拍国产 | 国内精品久久久久久久999下 | 日本黄色大片 | 宇都宫紫苑在线播放 | 老司机午夜性大片 | 日本强在线播放一区 | 欧美在线观看视频一区 | 一区二区三区鲁丝不卡麻豆 | 一本大道道香蕉免费 | 国产一级第一级毛片 | 日本三级午夜理伦三级三 | porn在线视频一区二区 | 女啪啪www女高清 | 99re热久久精品这里都是精品 | 91国内精品久久久久免费影院 | 中文字幕乱码六情 | 亚洲色大成网站www 亚洲色大成网站www久久九九 | 一级毛片在播放免费 | 免费永久国产在线视频 | xyx性爽欧美视频 | 亚洲图片小说网 | 黑人40厘米全进去xxxx猛交 | 天堂日本|