Java虛擬機的一些理解

我們知道Java是目前用戶最多,使用范圍最廣的軟件的開發(fā)技術之一。Java的技術體系可以分為以下三個方面組成:

  • Java虛擬機(JVM)

  • Java API 接口文檔

  • Java 編程語言以及許多Java框架

其中JVM是打造Java跨平臺的關鍵,但相比Java API接口文檔和Java本身編程語言,Java虛擬機相關的資料則顯得異常匱乏。Java虛擬機隱藏了底層技術的復雜性以及機器與操作系統(tǒng)的差異性,而為千萬開發(fā)者建立起使用方便的跨平臺開發(fā)框架,哪怕運行程序的物理機器的情況千差萬別,但Java虛擬機則在這千差萬別的物理機上建立了統(tǒng)一的運行平臺,從而使得開發(fā)者只需聚焦他們的業(yè)務程序。

正是這個跨平臺機制,實現(xiàn)了再任何一臺虛擬機上編譯的程序都能在任何一臺虛擬機上正常運行,這一極大優(yōu)勢使得Java應用的開發(fā)比傳統(tǒng)的C/C++應用開發(fā)來的更加高效,也導致Java技術棧能力圈越來越廣。也正好是Java虛擬機良好的封裝,作為開發(fā)者如果僅僅限于使用方便的API上,而不是去理解Java世界里真正的核心是什么,那么能力其實是難以進一步提高的,因此去了解Java虛擬機來龍去脈是很有必要的。

思維導圖

思維導圖

簡單做個思維導圖,這篇文章主要講的正如圖中所示幾個方面:JVM簡介、JVM內存運行機制、虛擬機類。

JVM簡介

Java為何能獲得如此廣泛的應用,除了它是擁有一門結構嚴謹、面向對象的編程語言之外,還有一點是脫離了硬件平臺的束縛,真正實現(xiàn)了「一次編寫,到處運行」的局面,并且提供了一個相對安全的內存管理和訪問機制,避免了絕大部分的內存泄漏和指針越界問題,這些好處就是統(tǒng)一放在Java虛擬機中。

發(fā)展歷史

從1996年Sun公司發(fā)布的JDK1.0版本以來,最早期是Sun Classic VM,到大名鼎鼎的HotSpot VM,然后進入到移動設備的Google Android Dalvik VM,還有其他VM,包括Microsoft JVM等等。其中最有名莫過于HotSpot VM和Google Android Dalvik VM,HotSpot VM是當前使用范圍最廣的Java虛擬機,它的熱點代碼探測技術,它在優(yōu)化程序的響應時間和最佳執(zhí)行性能獲得平衡,都使得它聲名大噪。

而Google Android Dalvik VM則是因為過去10年移動互聯(lián)網(wǎng)大熱,搭載Android系統(tǒng)的移動設備幾十億臺迅猛發(fā)展。本質上說 Dalvik VM并不是真正算的上一個Java虛擬機,因為它沒有遵循Java虛擬機規(guī)范,不能執(zhí)行Java的Class文件,但是它的Dex文件可以通過Class 文件轉化而來,使用Java語法編寫應用程序,可以直接使用大部分的Java API。

做什么

Java虛擬機主要能做的是提供一種跨平臺開發(fā)框架,讓使用者一次編寫的程序,就能在各個平臺上到處運行,使得開發(fā)者與硬件平臺脫離,并且提供的自動內存管理機制和運行時編譯優(yōu)化,進而使得應用Java應用隨著運行時間增加而獲得更高的性能。

JVM內存運行機制

運行時數(shù)據(jù)區(qū)域

Java虛擬機在執(zhí)行Java程序的過程中會把它所管理的內存劃分為若干個不同的數(shù)據(jù)區(qū)域,主要包括以下幾個運行時區(qū)域:

1,程序計數(shù)器,主要是當前線程所執(zhí)行的字節(jié)碼的行號指示器。
2,Java虛擬機棧,Java中的每個方法在執(zhí)行的同時都會創(chuàng)建一個棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。我們常見的StackOverflowError錯誤,就是常見棧深度大于虛擬機所允許的深度。
3,本地方法棧,執(zhí)行的是虛擬機使用到Nativie方法服務。
4,Java堆,也叫Java Heap,這個是Java虛擬機所管理的內存中最大的一塊,Java堆是被所有線程共享的一塊內存區(qū)域,在虛擬機啟動時就創(chuàng)建了。幾乎所有的對象實例都在這里分配內存。
5,方法區(qū)。這個是主要是用以虛擬機加載類信息、常量、靜態(tài)變量、編譯之后的代碼等等。

內存管理方式

要知道Java是以什么聞名嗎,當然是Java虛擬機的內存管理,也就是垃圾收集(GC),它的內存動態(tài)分配和內存回收技術已經(jīng)相當成熟,看起來這么完善了,為何還要去學習Java虛擬機,主要目的是為了在排查各種內存溢出、內存泄漏問題時,我們可以快速定位出問題,并且優(yōu)化和監(jiān)控。

在實例對象時,如何確定對象是否已死,有兩種方式介紹下:

1,引用計數(shù)算法。給實例對象添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器值就加1,當引用失效時,計數(shù)器值就減1。這個引用計數(shù)算法雖然判斷效率很高,但是有個問題是它很難解決對象之間的相互循環(huán)引用問題。

2,可達分析算法。這個算法思路主要是判定對象是否存活,從GC Roots的對象作為起點,從這個節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈,當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。這就引出了Java中的強引用、軟引用、弱引用、虛引用這四個區(qū)別。

說到垃圾收集算法,至于如何實現(xiàn),大家不妨有空去看看源碼,這里主要介紹算法的思想:

1,標記-清除算法。這里就包含兩個階段,“標記”和“清除”,首先標記出所需要回收的對象,在標記完成后統(tǒng)一回收所有被標記的對象。這個算法的不足之點在于效率問題,另一個是空間問題,標記清除之后會產(chǎn)生大量的不連續(xù)內存碎片,因為碎片,會容易引發(fā)另一次的垃圾收集動作。

2,復制算法。這個就解決碎片的問題,它將可用內存按容量劃分大小相等的兩塊,每次只使用其中的一塊,當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把使用過的內存空間一次清理掉,但這個算法的問題是容易造成內存浪費。

3,標記-整理算法。這個算法就結合前面兩個算法的特點,避免它們的弊端,先標記,但是后續(xù)不是直接對可回收對象進行清理,而是讓所有存活的對象向一端移動,然后直接清理掉端邊界以外的內存。

4,分代收集算法。當前商業(yè)虛擬機的垃圾收集都采用“分代收集”算法,這種主要根據(jù)對象存活周期的不同將內存劃分幾塊。一般把Java堆分為新生代和老年代,這樣就根據(jù)各個年代的特點去采用最適當?shù)氖占惴?。在新生代中,每次垃圾收集時都發(fā)現(xiàn)有大批對象死去,只有少量存活,那就選用復制算法,只需付出少量存活對象的復制成本就可以完成收集。而老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記-清理”或“標記-整理”算法來進行回收。

至于內存分配和回收策略,主要是根據(jù)分代收集算法,一般對象優(yōu)先在Eden分配,如果是大實例對象的話,則直接進入到老年代,還有長期存活的對象將進入老年代。

虛擬機類

Java內存的自動管理機制雖是舉世聞名,但是另外一個機制也是不甘落后,那就是類加載機制。在Java語言中,類型的加載、連接和初始化過程都是在程序運行期間完成的,雖然這種方式會使得類加載稍微增加一點性能開銷,但是給Java應用程序提供高度的靈活性,比如依賴運行期動態(tài)加載和動態(tài)鏈接這個特點實現(xiàn)的。這個特點在OSGi技術中體現(xiàn)的淋漓盡致。

類從被加載到虛擬機內存開始,到卸載出內存為止,它的整個生命周期包括:加載、驗證、準備、解析、初始化、使用和卸載這7個階段。一般在遇到new、getstatic、putstatic和invokestatic這4條字節(jié)碼指令時,如果類沒有進行過初始化,則需要先觸發(fā)其初始化。在加載階段,虛擬機主要完成以下3件事:

  • 通過一個類的全限定名來獲取定義此類的二進制字節(jié)流。

  • 將這個字節(jié)流所代表的靜態(tài)存儲結構轉化為方法區(qū)的運行時數(shù)據(jù)結構。

  • 在內存中生成一個代表這個類的java.lang.class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口。

而對類加載器,需要重點去了解它的雙親委派模型,從Java虛擬機角度來看,只存在兩種不同的類加載器,一種是啟動類加載器(Bootstarp ClassLoader),這個類加載器是C++語言實現(xiàn)的,是虛擬機自身的一部分,另一個就是所有其他的類加載器,這些類加載器都由Java語言實現(xiàn),獨立于虛擬機外部,并且全部繼承自抽象類java.lang.ClassLoader。

雙親委派模型的工作過程是:如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最終都應該傳遞到頂?shù)膯宇惣虞d器中,只有當父加載器反饋自己無法完成這個加載請求時,子加載器才會嘗試自己去加載。這個雙親委派模式帶來的一個優(yōu)勢會先檢查父加載器是否已經(jīng)被加載過,從這點衍生出Java世界的很多偉大技術出來,比如代碼熱替換,模塊熱部署,就是即插即用。

小結

通過了解Java虛擬機,對于Java虛擬機的運行機制有一定的了解,當然關于Java虛擬機內容還有很多要挖掘,比如性能調優(yōu)參數(shù)、調度、垃圾回收算法實現(xiàn)方式等等,對于作為一個Android開放人員來說,學習Java虛擬機更多是為了豐富自己的專業(yè)技術維度,擴展邊界。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容