java虛擬機

什麼是Java虛擬機?
Java虛擬機,從字面上來看,像是某種機器,但Java虛擬機之所以被稱之爲“虛擬”的,是因爲它是由一個規範來定義的抽象計算機,所以在我們說Java虛擬機的時候,可能指的是如下三種不同的東西:
  抽象規範
  一個具體的實現
  一個運行中的虛擬機實例

Java虛擬機的生命週期

  當啓動一個Java程序時,一個虛擬機實例也就誕生了。該程序關閉退出時,虛擬機實例也就隨之消亡。Java虛擬機通過調用某個初始類的main()方法作爲Java程序運行的起點。在Java虛擬機內容,有兩種線程,守護線程和非守護線程。守護線程一般爲虛擬機自己使用,比如垃圾收集線程,非守護線程比如運行main()的線程。當程序中所有非守護線程終止時,則虛擬機實例自動退出。

Java虛擬機的體系結構

  在Java虛擬機規範中,一個虛擬機實例的行爲按照子系統、內存區、數據類型以及指令幾個術語來描述。每個Java虛擬機都有一個類裝載子系統,它根據全限定名來裝入系統,同樣,每個Java虛擬機都有一個執行引擎,它負責執行那些包含在被裝載類的方法中的指令。
java虛擬機
當Java虛擬機運行一個程序時,它需要內存來存儲許多東西,比如,字節碼、對象、局部變量、運算的中間結果等。某些運行時數據區由程序中所有線程共享,還有一些只能由一個線程擁有。每個虛擬機都有一個方法區和一個堆,他們是由該虛擬機中所有線程共享的。當虛擬機裝載一個class文件時,會把類型信息存入方法區,程序運行時虛擬機會把運行時創建的對象存入堆中。

  每當一個新線程創建的時候,它都將獲得它自己的PC寄存器和一個Java棧,若線程正在執行一個Java方法(非本地方法),那麼PC計數器的值總是指示下一條將被執行的指令,Java棧則包含線程中Java方法的調用狀態—-包括它的局部變量,被調用時傳進來的參數、它的返回值、計算的中間結果等。本地方法調用,則是以某種依賴於具體實現的方式存儲在本地方法棧中,也可能是寄存器和其他某些與特定實現相關的內存區。

  Java棧是由許多棧幀或者說幀組成,一個棧幀包含一個Java方法的調用狀態。當線程調用一個Java方法時,虛擬機壓入一個新的棧幀到棧中,方法返回後此棧幀彈出並拋棄。
  
  數據類型

  數據類型可分爲兩類:基本類型和引用類型,基本類型持有原始值,引用類型持有引用值。Java基本類型的值域在任何地方都是一致的,比如一個long類型在任何虛擬機中都是64位二進制補碼錶示的有符號整數。
java虛擬機
注意:boolean有些特別,雖然boolean也是基本類型,但是在編譯爲字節碼時,它會用int或者byte來表示boolean,false表示爲整數0,true爲整數1。另外boolean數組是被當做byte數組類訪問的。
java虛擬機
字長的考量

  Java虛擬機中,最基本的數據單元就是字,虛擬機實現者最少選擇32位作爲字長,或者選擇更爲高效的字長。通常根據底層主機平臺的指針長度來選擇字長。

類裝載器子系統

  Java虛擬機中有兩種類裝載器:啓動類裝載器和用戶自定義裝載器。前者是Java虛擬機實現的一部分,後者是Java程序的一部分。不同類裝載器放在虛擬機內部的不同命名空間中。

  ClassLoader中定義的方法爲程序提供了訪問類裝載器機制的接口。每一個被裝載的類型,Java虛擬機都會爲它創建一個java.lang.Class類的實例來代表該類型。用戶自定義的類裝載器以及Class類的實例都放在內存中的堆區,而裝載的類型信息則都位於方法區。

  類裝載器除了要定位和導入二進制class文件外,還需要負責導入類的正確性,分配變量和初始化內存,解析符號引用等。這些動作必須嚴格按照以下步驟進行:

  1、裝載 ——– 查找並載入二進制數據

  2、連接 ——– 執行驗證,準備,已經解析(可選)

    驗證: 確保被導入類型的正確性

    準備:爲類變量分配內存,並將其初始化爲默認值

    解析:把類型中的符號引用轉換爲直接引用

  3、初始化 ——– 把類變量初始化爲正確的初始值

方法區

  在Java虛擬機中,關於被裝載類型的信息存儲在一個邏輯上被稱爲方法區的內存中。當虛擬機裝載某個類型時,首先使用類裝載器定位相應的class文件,然後讀入class文件 ——- 一個線性二進制數據流,然後將它傳輸到虛擬機中。之後虛擬機提取出其中的類型信息存入方法區,同時該類中的靜態變量也是存儲在方法區中。

  所有線程都共享方法區,所以它們對方法區的訪問必須爲線程安全的,比如,如果兩個線程同時都企圖訪問名爲Lava的類,而此類尚未裝載進虛擬機,那麼,這時只應該有一個線程去裝載它,另一個只能等待。

  方法區大小不是固定的,可以根據需要自己調整。同樣方法區也不必是連續的,方法區可以在同一個堆中自由分配,也可以由程序員指定方法區的初始大小的最大尺寸和最小尺寸等。

  方法區也可以被垃圾收集,虛擬機允許用戶定義的類裝載器來動態擴展Java程序(反射),因此一些類也會成爲程序“不再引用”的類。當某個類不再被引用時,Java虛擬機可以卸載此類。

  對應每個裝載的類型,虛擬機會在方法區存儲以下類型信息:

    此類的全限定名

    此類的直接超類全限定名

    此類是類類型還是接口類型

    此類的訪問修飾符  

    任何直接超類的全限定名的有序列表

  除以上列出的基本類型信息,虛擬機還得爲每個裝載的類型存儲以下信息

    該類型的常量池  

    字段信息

    方法信息

    除了常量以外所有類的(靜態)變量

    一個到類的ClassLoader引用

    一個到Class類的引用

  Java程序運行時創建的所有類實例和數組都放在同一個堆中,而一個Java虛擬機實例中只有存在一個堆空間,因此所有線程都將共享這個堆。由於每一個Java程序獨佔一個堆空間,因此所有的線程將共享這個堆。但是同一個程序的多個線程卻共享着同一個空間,此種情況下,需要考慮多線程訪問對象(堆數據)的同步問題。

  Java虛擬機有一條在堆中分配新對象的指令,卻沒有釋放內存的指令。正如我們無法用Java代碼去明確釋放一個對象一樣,字節碼中也沒有相關功能。需要虛擬機自己負責決定如何已及何時開始垃圾收集。程序本身也不需要關心何時回收,通常虛擬機把這個任務交給垃圾收集器。

  Java虛擬機規範並沒有規定Java對象在堆中是如何表示的。對象的內部表示影響整個堆以及垃圾收集器的設計,它由虛擬機的實現者決定。

  一種可能的堆空間設計爲,把堆分爲兩個部分:一個句柄池,一個對象池,而一個對象引用則是指向句柄池的本地指針。句柄池分爲兩個部分:一個指向對象實例變量的指針,一個指向方法區類型的指針。這種設計的有點爲有利於堆碎片的整理,缺點爲每次訪問對象都要經過兩次指針的傳遞。
java虛擬機

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章