大型互聯網企業深入理解Java虛擬機結構體系

引言

Java虛擬機是一種抽象化的計算機,通過在實際的計算機上仿真模擬各種計算機功能來實現的。Java虛擬機有自己完善的硬體架構,如處理器、堆棧、寄存器等,還具有相應的指令系統。Java虛擬機屏蔽了與具體操作系統平臺相關的信息,使得Java程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。

什麼是Java虛擬機?

簡單的說,一個JVM是一個軟件模塊,用於執行Java應用字節碼並且把字節碼轉化到硬件,操作系統特殊指令。通過這樣做,JVM允許Java程序在第一次編寫後可以在不同的環境中執行,並不需要更改原始的代碼。Java的可移植性是通往企業應用語言的關鍵:開發者並不需要為不同平臺重寫應用代碼,因為JVM負責翻譯和平臺優化。

一個JVM基本上是一個虛擬的執行環境,作為一個字節碼指令機器,而用於分配執行任務和執行內存操作通過與底層的交互。

一個JVM同樣為運行的Java應用照看動態資源管理。這就意味著它掌握分配和釋放內存,在每個平臺上保持一致的線程模型,在應用執行的地方用一種適於CPU架構的方式組織可執行的指令。JVM把開發人員從跟蹤對象當中的引用,和它們需要在系統中存在多長時間中解放出來。同樣的它不用我們管理何時去釋放內存——一個像C語言那樣的非動態語言的痛點。

你可以把JVM當做是一個專門為Java運行的操作系統;它的工作是為Java應用管理運行環境。一個JVM基本上是一個虛擬的通過與底層的交互的執行環境,作為一個字節碼指令機器,而用於分配執行任務和執行內存操作。

JVM組件概述

有很多寫JVM內部和性能優化的文章。作為這個系列的基礎,我將會總結概述下JVM組件。這個簡短的閱覽會為剛接觸JVM的開發者有特殊的幫助,會讓你更想了解之後更深入的討論。

JAVA虛擬機的體系結構

下圖是JAVA虛擬機的結構圖,每個Java虛擬機都有一個類裝載子系統,它根據給定的全限定名來裝入類型(類或接口)。同樣,每個Java虛擬機都有一個執行引擎,它負責執行那些包含在被裝載類的方法中的指令。

大型互聯網企業深入理解Java虛擬機結構體系

當JAVA虛擬機運行一個程序時,它需要內存來存儲許多東西,例如:字節碼、從已裝載的class文件中得到的其他信息、程序創建的對象、傳遞給方法的參數,返回值、局部變量等等。Java虛擬機把這些東西都組織到幾個“運行時數據區”中,以便於管理。

某些運行時數據區是由程序中所有線程共享的,還有一些則只能由一個線程擁有。每個Java虛擬機實例都有一個方法區以及一個堆,它們是由該虛擬機實例中所有的線程共享的。當虛擬機裝載一個class文件時,它會從這個class文件包含的二進制數據中解析類型信息。然後把這些類型信息放到方法區中。當程序運行時,虛擬機會把所有該程序在運行時創建的對象都放到堆中。

數據類型

Java虛擬機是通過某些數據類型來執行計算的,數據類型可以分為兩種:基本類型和引用類型,基本類型的變量持有原始值,而引用類型的變量持有引用值。

大型互聯網企業深入理解Java虛擬機結構體系

Java語言中的所有基本類型同樣也都是Java虛擬機中的基本類型。但是boolean有點特別,雖然Java虛擬機也把boolean看做基本類型,但是指令集對boolean只有很有限的支持,當編譯器把Java源代碼編譯為字節碼時,它會用int或者byte來表示boolean。在Java虛擬機中,false是由整數零來表示的,所有非零整數都表示true,涉及boolean值的操作則會使用int。另外,boolean數組是當做byte數組來訪問的,但是在“堆”區,它也可以被表示為位域。

Java虛擬機還有一個只在內部使用的基本類型:returnAddress,Java程序員不能使用這個類型,這個基本類型被用來實現Java程序中的finally子句。該類型是jsr, ret以及jsr_w指令需要使用到的,它的值是JVM指令的操作碼的指針。returnAddress類型不是簡單意義上的數值,不屬於任何一種基本類型,並且它的值是不能被運行中的程序所修改的。

Java虛擬機的引用類型被統稱為“引用(reference)”,有三種引用類型:類類型、接口類型、以及數組類型,它們的值都是對動態創建對象的引用。類類型的值是對類實例的引用;數組類型的值是對數組對象的引用,在Java虛擬機中,數組是個真正的對象;而接口類型的值,則是對實現了該接口的某個類實例的引用。還有一種特殊的引用值是null,它表示該引用變量沒有引用任何對象。

Java虛擬機規範中沒有任何OutOfMemoryError情況的區域

程序計數器是一塊較小的內存空間,它可以看作是當前線程所執行的字節碼的行號指示器。在虛擬機的概念模型(僅在模型概念裡,各種虛擬機可能通過更高效的方式去實現)裡,字節碼解釋器工作時就是通過改變這個計數器的值來選取下一條需要執行的字節碼指令,分支、循環、跳轉、異常處理、線程恢復等基礎功能都需要依賴這個計數器來完成。

由於Java虛擬機的多線程是通過 線程輪流切換並分配處理器執行時間的方式來實現的,在任何一個確定的時刻,一個處理器(對於多核處理器來說是一個內核),都會只執行一條線程中的指令。因此,為了線程切換後能恢復到正確的執行位置,每條線程都需要一個獨立的程序計數器,各條線程之間計數器互不影響,獨立存儲,我們稱這類內存區域為“線程私有”的內存。

如果線程正在執行一個是一個Java方法,這個計數器記錄的是正在執行的虛擬機字節碼指令的地址;如果正在執行的是Native方法,這個計數器值則為空(Undefined)。

Java虛擬機棧

與程序計數器一樣,Java虛擬機棧也是線程私有的,它的生命週期與線程相同。虛擬機棧描述的是Java方法執行的內存模型:每個方法在執行的同時都會創建一個棧幀(Stack Frame)用於存儲 局部變量表、操作數棧、動態鏈接、方法出口 等信息。每一個方法從調用直至執行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。

經常有人把Java內存區分為堆內存(Heap)和棧內存(Stack),這種分法比較粗糙,Java內存區域的劃分實際上遠閉著複雜。這裡所指的“棧”就是虛擬機棧,或者說是虛擬機棧中的局部變量表部分。

局部變量表存放了編譯期可知的各種基本數據類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等於對象本身,可能是一個指向對象起止地址的引用指針,也可能是指向一個代表對象的句柄或者其他與此對象相關的位置)和returnAddress(指向一條字節碼指令的地址)。

其中64位長度的 long 和 double 類型的數據會佔用2個局部變量空間(Slot),其餘數據類型只佔用1個。局部變量所需的內存空間在編譯期間完成分配,當進入一個方法時,這個方法需要的在幀中分配的局部變量空間是完全確定的,在方法運行期間不會改變局部變量表的大小。

在Java虛擬機規範中,對這個區域規定了兩種異常情況:如果線程請求的棧深度大於虛擬機所允許的深度,將拋出StackOverflowError異常;如果虛擬機棧可以動態擴展(當前大部分虛擬機都可以動態擴展,只不過Java虛擬機規範中也允許固定長度的虛擬機棧),如果擴展時無法申請到足夠的內存,就會拋出OutOfMemoryError異常。

總結

以 上就是我對大型互聯網企業深入理解Java虛擬機結構體系問題及其優化總結,分享給大家,希望大家知道什麼是大型互聯網企業深入理解Java虛擬機結構體系問題及其優化。覺得收穫的話可以點個關注收藏轉發一波喔,謝謝大佬們支持!

  • 1、多寫多敲代碼,好的代碼與紮實的基礎知識一定是實踐出來的

  • 2、可以去百度搜索騰訊課堂圖靈學院的視頻來學習一下java架構實戰案例,還挺不錯的。

  • 最後,每一位讀到這裡的網友,感謝你們能耐心地看完。希望在成為一名更優秀的Java程序員的道路上,我們可以一起學習、一起進步!都能贏取白富美,走向架構師的人生巔峰!

  • 3丶想了解學習以上課程內容可加群:469717771 驗證碼頭條(06 必過)歡迎大家的加入喲!

大型互聯網企業深入理解Java虛擬機結構體系

相關推薦

推薦中...