久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

淺析Java虛擬機結構與機制(轉)

 博雅書屋lhs 2015-01-18

本文旨在給所有希望了解JVM(Java Virtual Machine)的同學一個概念性的入門,,主要介紹了JVM的組成部分以及它們內部工作的機制和原理。當然本文只是一個簡單的入門,,不會涉及過多繁雜的參 數和配置,,感興趣的同學可以做更深入的研究,在研究JVM的過程中會發(fā)現,,其實JVM本身就是一個計算機體系結構,,很多原理和我們平時的硬件、微機原理,、 操作系統(tǒng)都有十分相似的地方,,所以學習JVM本身也是加深自我對計算機結構認識的一個很好的途徑,。

另外需要注意的是,雖然平時我們用的大多是Sun(現已被Oracle收購)JDK提供的JVM,,但是JVM本身是一個規(guī)范,,所以可以有多種實現,除了Hotspot外,,還有諸如Oracle的JRockit,、IBM的J9也都是非常有名的JVM。

一,、JVM結構

下圖展示了JVM的主要結構:

可以看出,,JVM主要由類加載器子系統(tǒng)、運行時數據區(qū)(內存空間),、執(zhí)行引擎以及與本地方法接口等組成。其中運行時數據區(qū)又由方法區(qū),、堆,、Java棧、PC寄存器,、本地方法棧組成,。

從上圖中還可以看出,在內存空間中方法區(qū)和堆是所有Java線程共享的,,而Java棧,、本地方法棧、PC寄存器則由每個線程私有,,這會引出一些問題,,后文會進行具體討論。

眾所周知,,Java語言具有跨平臺的特性,,這也是由JVM來實現的。更準確地說,,是Sun利用JVM在不同平臺上的實現幫我們把平臺相關性的問題給 解決了,,這就好比是HTML語言可以在不同廠商的瀏覽器上呈現元素(雖然某些瀏覽器在對W3C標準的支持上還有一些問題)。同時,,Java語言支持通過 JNI(Java Native Interface)來實現本地方法的調用,,但是需要注意到,如果你在Java程序用調用了本地方法,,那么你的程序就很可能不再具有跨平臺性,,即本地方法 會破壞平臺無關性。

二,、類加載器子系統(tǒng)(Class Loader)

類加載器子系統(tǒng)負責加載編譯好的.class字節(jié)碼文件,,并裝入內存,,使JVM可以實例化或以其它方式使用加載后的類。 JVM的類加載子系統(tǒng)支持在運行時的動態(tài)加載,,動態(tài)加載的優(yōu)點有很多,,例如可以節(jié)省內存空間、靈活地從網絡上加載類,,動態(tài)加載的另一好處是可以通過命名空 間的分隔來實現類的隔離,,增強了整個系統(tǒng)的安全性。

1,、ClassLoader的分類:

a.啟動類加載器(BootStrap Class Loader):負責加載rt.jar文件中所有的Java類,,即Java的核心類都是由該ClassLoader加載。在Sun JDK中,,這個類加載器是由C++實現的,,并且在Java語言中無法獲得它的引用。

b.擴展類加載器(Extension Class Loader):負責加載一些擴展功能的jar包,。

c.系統(tǒng)類加載器(System Class Loader):負責加載啟動參數中指定的Classpath中的jar包及目錄,,通常我們自己寫的Java類也是由該ClassLoader加載。在Sun JDK中,,系統(tǒng)類加載器的名字叫AppClassLoader,。

d.用戶自定義類加載器(User Defined Class Loader):由用戶自定義類的加載規(guī)則,可以手動控制加載過程中的步驟,。

2,、ClassLoader的工作原理

類加載分為裝載、鏈接,、初始化三步,。

a.裝載

通過類的全限定名和ClassLoader加載類,主要是將指定的.class文件加載至JVM,。當類被加載以后,,在JVM內部就以“類的全限定名+ClassLoader實例ID”來標明類。

在內存中,,ClassLoader實例和類的實例都位于堆中,,它們的類信息都位于方法區(qū)。

裝載過程采用了一種被稱為“雙親委派模型(Parent Delegation Model)” 的方式,,當一個ClassLoader要加載類時,,它會先請求它的雙親ClassLoader(其實這里只有兩個ClassLoader,所以稱為父 ClassLoader可能更容易理解)加載類,,而它的雙親ClassLoader會繼續(xù)把加載請求提交再上一級的ClassLoader,,直到啟動類加 載器。只有其雙親ClassLoader無法加載指定的類時,,它才會自己加載類,。

雙親委派模型是JVM的第一道安全防線,,它保證了類的安全加載,這里同時依賴了類加載器隔離的原理:不同類加載器加載的類之間是無法直接交互的,,即 使是同一個類,,被不同的ClassLoader加載,它們也無法感知到彼此的存在,。這樣即使有惡意的類冒充自己在核心包(例如java.lang)下,,由 于它無法被啟動類加載器加載,也造成不了危害,。

由此也可見,,如果用戶自定義了類加載器,那就必須自己保障類加載過程中的安全,。

b.鏈接

鏈接的任務是把二進制的類型信息合并到JVM運行時狀態(tài)中去,。

鏈接分為以下三步:

a.驗證:校驗.class文件的正確性,確保該文件是符合規(guī)范定義的,,并且適合當前JVM使用,。

b.準備:為類分配內存,同時初始化類中的靜態(tài)變量賦值為默認值,。

c.解析(可選):主要是把類的常量池中的符號引用解析為直接引用,這一步可以在用到相應的引用時再解析,。

c.初始化

初始化類中的靜態(tài)變量,,并執(zhí)行類中的static代碼、構造函數,。

JVM規(guī)范嚴格定義了何時需要對類進行初始化:

a,、通過new關鍵字、反射,、clone,、反序列化機制實例化對象時。

b,、調用類的靜態(tài)方法時,。

c、使用類的靜態(tài)字段或對其賦值時,。

d,、通過反射調用類的方法時。

e,、初始化該類的子類時(初始化子類前其父類必須已經被初始化),。

f、JVM啟動時被標記為啟動類的類(簡單理解為具有main方法的類),。

三,、Java棧(Java Stack)

Java棧由棧幀組成,,一個幀對應一個方法調用。調用方法時壓入棧幀,,方法返回時彈出棧幀并拋棄,。Java棧的主要任務是 存儲方法參數、局部變量,、中間運算結果,,并且提供部分其它模塊工作需要的數據。前面已經提到Java棧是線程私有的,,這就保證了線程安全性,,使得程序員無 需考慮棧同步訪問的問題,只有線程本身可以訪問它自己的局部變量區(qū),。

它分為三部分:局部變量區(qū),、操作數棧、幀數據區(qū),。

1,、局部變量區(qū)

局部變量區(qū)是以字長為單位的數組,在這里,,byte,、short、char類型會被轉換成int類型存儲,,除了long和 double類型占兩個字長以外,,其余類型都只占用一個字長。特別地,,boolean類型在編譯時會被轉換成int或byte類型,,boolean數組會 被當做byte類型數組來處理。局部變量區(qū)也會包含對象的引用,,包括類引用,、接口引用以及數組引用。

局部變量區(qū)包含了方法參數和局部變量,,此外,,實例方法隱含第一個局部變量this,它指向調用該方法的對象引用,。對于對象,,局部變量區(qū)中永遠只有指向堆的引用。

2,、操作數棧

操作數棧也是以字長為單位的數組,,但是正如其名,它只能進行入棧出棧的基本操作。在進行計算時,,操作數被彈出棧,,計算完畢后再入棧。

3,、幀數據區(qū)

幀數據區(qū)的任務主要有:

a.記錄指向類的常量池的指針,,以便于解析。

b.幫助方法的正常返回,,包括恢復調用該方法的棧幀,,設置PC寄存器指向調用方法對應的下一條指令,把返回值壓入調用棧幀的操作數棧中,。

c.記錄異常表,,發(fā)生異常時將控制權交由對應異常的catch子句,如果沒有找到對應的catch子句,,會恢復調用方法的棧幀并重新拋出異常,。

局部變量區(qū)和操作數棧的大小依照具體方法在編譯時就已經確定。調用方法時會從方法區(qū)中找到對應類的類型信息,,從中得到具體方法的局部變量區(qū)和操作數棧的大小,,依此分配棧幀內存,壓入Java棧,。

四,、本地方法棧(Native Method Stack)

本地方法棧類似于Java棧,主要存儲了本地方法調用的狀態(tài),。在Sun JDK中,,本地方法棧和Java棧是同一個。

五,、方法區(qū)(Method Area)

類型信息和類的靜態(tài)變量都存儲在方法區(qū)中。方法區(qū)中對于每個類存儲了以下數據:

a.類及其父類的全限定名(java.lang.Object沒有父類)

b.類的類型(Class or Interface)

c.訪問修飾符(public, abstract, final)

d.實現的接口的全限定名的列表

e.常量池

f.字段信息

g.方法信息

h.靜態(tài)變量

i.ClassLoader引用

j.Class引用

可見類的所有信息都存儲在方法區(qū)中,。由于方法區(qū)是所有線程共享的,,所以必須保證線程安全,舉例來說,,如果兩個類同時要加載一個尚未被加載的類,,那么一個類會請求它的ClassLoader去加載需要的類,另一個類只能等待而不會重復加載,。

此外為了加快調用方法的速度,,通常還會為每個非抽象類創(chuàng)建私有的方法表,方法表是一個數組,,存放了實例可能被調用的實例方法的直接引用,。方法表對于多態(tài)有非常重要的意義,具體可以參照《淺談多態(tài)機制的意義及實現》一文中“多態(tài)的實現”一節(jié)。

在Sun JDK中,,方法區(qū)對應了持久代(Permanent Generation),,默認最小值為16MB,最大值為64MB,。

六,、堆(Heap)

堆用于存儲對象實例以及數組值。堆中有指向類數據的指針,,該指針指向了方法區(qū)中對應的類型信息,。堆中還可能存放了指向方法 表的指針。堆是所有線程共享的,,所以在進行實例化對象等操作時,,需要解決同步問題。此外,,堆中的實例數據中還包含了對象鎖,,并且針對不同的垃圾收集策略, 可能存放了引用計數或清掃標記等數據,。

在堆的管理上,,Sun JDK從1.2版本開始引入了分代管理的方式。主要分為新生代,、舊生代,。分代方式大大改善了垃圾收集的效率。

1,、新生代(New Generation)

大多數情況下新對象都被分配在新生代中,,新生代由Eden Space和兩塊相同大小的Survivor Space組成,后兩者主要用于Minor GC時的對象復制(Minor GC的過程在此不詳細討論),。

JVM在Eden Space中會開辟一小塊獨立的TLAB(Thread Local Allocation Buffer)區(qū)域用于更高效的內存分配,,我們知道在堆上分配內存需要鎖定整個堆,而在TLAB上則不需要,,JVM在分配對象時會盡量在TLAB上分配,,以提高效率。

2,、舊生代(Old Generation/Tenuring Generation)

在新生代中存活時間較久的對象將會被轉入舊生代,,舊生代進行垃圾收集的頻率沒有新生代高。

七,、執(zhí)行引擎

執(zhí)行引擎是JVM執(zhí)行Java字節(jié)碼的核心,,執(zhí)行方式主要分為解釋執(zhí)行、編譯執(zhí)行,、自適應優(yōu)化執(zhí)行,、硬件芯片執(zhí)行方式。

JVM的指令集是基于棧而非寄存器的,這樣做的好處在于可以使指令盡可能緊湊,,便于快速地在網絡上傳輸(別忘了Java最初就是為網絡設計的),,同 時也很容易適應通用寄存器較少的平臺,并且有利于代碼優(yōu)化,,由于Java棧和PC寄存器是線程私有的,,線程之間無法互相干涉彼此的棧。每個線程擁有獨立的 JVM執(zhí)行引擎實例,。

JVM指令由單字節(jié)操作碼和若干操作數組成,。對于需要操作數的指令,通常是先把操作數壓入操作數棧,,即使是對局部變量賦值,,也會先入棧再賦值。注意這里是“通?!鼻闆r,,之后會講到由于優(yōu)化導致的例外。

1,、解釋執(zhí)行

和一些動態(tài)語言類似,,JVM可以解釋執(zhí)行字節(jié)碼。Sun JDK采用了token-threading的方式,,感興趣的同學可以深入了解一下,。

解釋執(zhí)行中有幾種優(yōu)化方式:

a.棧頂緩存

將位于操作數棧頂的值直接緩存在寄存器上,對于大部分只需要一個操作數的指令而言,,就無需再入棧,,可以直接在寄存器上進行計算,結果壓入操作數站,。這樣便減少了寄存器和內存的交換開銷,。

b.部分棧幀共享

被調用方法可將調用方法棧幀中的操作數棧作為自己的局部變量區(qū),這樣在獲取方法參數時減少了復制參數的開銷,。

c.執(zhí)行機器指令

在一些特殊情況下,,JVM會執(zhí)行機器指令以提高速度。

2,、編譯執(zhí)行

為了提升執(zhí)行速度,Sun JDK提供了將字節(jié)碼編譯為機器指令的支持,,主要利用了JIT(Just-In-Time)編譯器在運行時進行編譯,,它會在第一次執(zhí)行時編譯字節(jié)碼為機器碼并緩存,之后就可以重復利用,。Oracle JRockit采用的是完全的編譯執(zhí)行,。

3、自適應優(yōu)化執(zhí)行

自適應優(yōu)化執(zhí)行的思想是程序中10%~20%的代碼占據了80%~90%的執(zhí)行時間,所以通過將那少部分代碼編譯為優(yōu)化過 的機器碼就可以大大提升執(zhí)行效率,。自適應優(yōu)化的典型代表是Sun的Hotspot VM,,正如其名,JVM會監(jiān)測代碼的執(zhí)行情況,,當判斷特定方法是瓶頸或熱點時,,將會啟動一個后臺線程,把該方法的字節(jié)碼編譯為極度優(yōu)化的,、靜態(tài)鏈接的 C++代碼,。當方法不再是熱區(qū)時,則會取消編譯過的代碼,,重新進行解釋執(zhí)行,。

自適應優(yōu)化不僅通過利用小部分的編譯時間獲得大部分的效率提升,而且由于在執(zhí)行過程中時刻監(jiān)測,,對內聯代碼等優(yōu)化也起到了很大的作用,。由于面向對象的多態(tài)性,一個方法可能對應了很多種不同實現,,自適應優(yōu)化就可以通過監(jiān)測只內聯那些用到的代碼,,大大減少了內聯函數的大小。

Sun JDK在編譯上采用了兩種模式:Client和Server模式,。前者較為輕量級,,占用內存較少。后者的優(yōu)化程序更高,,占用內存更多,。

在Server模式中會進行對象的逃逸分析,即方法中的對象是否會在方法外使用,,如果被其它方法使用了,,則該對象是逃逸的。對于非逃逸對象,,JVM 會在棧上直接分配對象(所以對象不一定是在堆上分配的),,線程獲取對象會更加快速,同時當方法返回時,,由于棧幀被拋棄,,也有利于對象的垃圾收集。 Server模式還會通過分析去除一些不必要的同步,,感興趣的同學可以研究一下Sun JDK 6引入的Biased Locking機制,。

此外,執(zhí)行引擎也必須保證線程安全性,,因而JMM(Java Memory Model)也是由執(zhí)行引擎確保的,。

    本站是提供個人知識管理的網絡存儲空間,,所有內容均由用戶發(fā)布,不代表本站觀點,。請注意甄別內容中的聯系方式,、誘導購買等信息,謹防詐騙,。如發(fā)現有害或侵權內容,,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多