java虛擬機

一.java虛擬機是什麼

可能指以下三種不同的東西

   1.抽象規範 僅僅是一個概念,規範的具體實現可能來自多個提供商,並存與多個平臺上

   2.一個具體實現

   3.一個運行中的虛擬機實例

 

二.java虛擬機的生命週期

   當啓動一個java程序時,一個虛擬機的實例就誕生了.關閉則虛擬機實例消亡.一臺機器運行多個java程序,將產生多個虛擬機實例.每個java程序都在他自己的實例中運行.

   java虛擬機調用main()方法來運行一個java程序.main()必須是public,static,void,並接受一個字符串作爲參數.任何滿足這個條件的類都可以作爲java程序運行的起點.

   java線程分爲守護線程和非守護線程.垃圾收集任務的線程屬於守護線程.java程序中的初始線程開始於main()的線程,是非守護線程.只要有任何非守護線程運行,那麼這個java程序也繼續運行.當該程序中所有的非守護線程終止時,虛擬機實例自動退出。假如安全管理器允許,程序本身也可以調用Runtime類或者System類的exit()方法來退出。

 

三.虛擬機的體系結構

 如下圖所示:

每個java虛擬機實例都有一個方法區以及一個堆,它們是由該虛擬機實例中所有線程共享。當虛擬機裝載class文件時,它會從這個class文件中讀取二進制數據並解析類型信息。然後,它把這些信息放到方法區中。當程序運行時,虛擬機把所有該程序運行時產生的對象都放進堆中。

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

 

Java棧:

       由許多棧幀(stack frame)或者說幀(frame)組成的,一個棧幀包含一個Java方法調用的狀態。當線程調用一個方法時,虛擬機壓入一個新的棧幀到該線程的Java棧種;當該方法返回時,這個棧幀被從Java棧中彈出並拋棄。

 

1.數據類型

   基本類型:

byte   8bit,帶符號,二進制補碼(-2的7次方到2的7次方-1,包括兩端的值在內)

short  16bit,帶符號,二進制補碼(-2的15次方到2的15次方-1,包括兩端的值在內)

int      32bit,帶符號,二進制補碼(-2的31次方到2的31次方-1,包括兩端的值在內)

long   64bit,帶符號,二進制補碼(-2的63次方到2的63次方-1,包括兩端的值在內)

float   32bit,IEEE754標準單精度浮點數

double 64bit,IEEE754標準雙精度浮點數

boolean(編譯後用int或者byte代表boolean,0代表FALSE,其它代表TRUE)

char 持有變量原始值

   引用類型:類類型,接口類型,數組類型,null(表示該變量沒有引用任何對象) 持有變量引用值

   Java中還有一個只在內部使用的基本類型 returnAddress,Java程序員不能使用這個類型,這個基本類型被用來實現Java程序中的finally子句。(同一方法中某操作碼的地址)

 

2.字長考量

   虛擬機實現者至少選擇32位作爲一個字長。

 

3.類裝載子系統

   在Java虛擬機中,負責查找並裝載類型的那部分稱爲類裝載子系統。

   Java虛擬機有兩種類裝載器:

          啓動類裝載器:JVM實現的一部分

          用戶自定義類裝載器:java程序的一部分,是普通的java對象,它派生自java.lang.ClassLoader。ClassLoader中定義的方法爲程序提供了裝載類機制的接口。每一個被裝載的類,虛擬機都爲它創建一個Class實例來代表該類型。類裝載器和Class實例都放在堆區,裝載的類型信息都放在方法區中。

   不同的類裝載器裝載的類被放在java虛擬機內不同的命名空間中。類裝載器子系統除了要定位和導入二進制class文件,還必須驗證被導入類的正確性,爲變量分配並初始化內存,以及幫助解析符號引用。嚴格按照以下順序動作:

     ●裝載:查找並裝載類型的二進制數據

     ●連接:執行驗證,準備以及解析

     ●初始化:把類變量初始化爲正確的值

 

4.方法區

   Java虛擬機中,關於被裝載的類型信息放在一個邏輯上被成爲方法區的內存中。當虛擬機運行Java程序時,它會查找使用存儲在方法區的類型信息。由於所有線程都是共享的,所以它們對方法區的數據訪問必須設計爲線程安全的。方法區大小不必是固定的,虛擬機可根據實際需要動態調整。也不必是連續的,方法區可以在一個堆中自由分配。虛擬機也可以允許用戶或者程序員制定方法區的初始大小以及最小和最大尺寸等。方法區可以被垃圾回收。

  方法區存儲的類型信息:

   ●這個類型的全限定名

   ●這個類型的直接超類的全限定名(除非這個類型是java.lang.Object,它沒有超類)。

   ●這個類型是類類型還是接口類型

   ●這個類型的訪問修飾符

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

   ●該類型的常量池:該類型的所有常量的一個有序結合,包括直接常量和對其他類型、字段和方法的符號引用

   ●字段信息:要保存字段名,字段類型,字段修飾符以及字段在接口或類中聲明的順序

   ●方法信息:要保存方法名,方法的返回類型,方法參數的類型和數量,方法的修飾符以及聲明的順序。如果方法不是抽象活本地的方法,還得保存方法的字節碼,操作數棧和方法的棧幀種的局部變量區的大小,異常表。

   ●除了常量以外的所有類(靜態)變量:編譯時常量(那些final聲明以及用編譯時已知的值初始化的類變量)??

   ●一個到類ClassLoader的引用

   ●一個到Class的引用

   ●方法表:爲了儘可能提高訪問效率,設計者必須仔細設計存儲在方法區中的類型信息的數據結構 ,因此除了以上原始類型信息以外,還可能存在快速訪問原始類型信息的數據結構,比如方法表。虛擬機對每個裝載的非抽象類都生成一個方法表,方法表是一個數組,它的元素就是所有它的實例可能被調用的實例方法的引用,包括那些超類繼承的實例方法。接口和抽象類沒有方法表,因爲不會生成實例。

              方法表是一個指針數組,其中的每一項都是一個指向“實例方法數據”的指針,實例方法可以被那類的對象吊用。方法表指向的實例方法數據包括以下的數據:

        此方法的操作數棧和局部變量的大小

        此方法的字節碼

        異常表

 

5.堆:Java程序在運行時所創建的所有類實例或數組都放在同一個堆中。某些實現允許用戶或程序員制定堆的初始大小、最大最小值等等。堆的內部表示:堆除了包括本身對象的數據外還包括要實現鎖所需要的數據以及實現等待集合的數據相關聯。鎖是用來實現多個線程對共享數據的互斥訪問的,等待集合時讓多個線程爲了完成一個共同的目標而協調工作的。最後一種是與垃圾收集器相關的數據

   數組的內部表示:堆中的每個數組對象還必須保存數組的長度,數組數據,以及某些指向數組的類數據的引用。

 

6.程序計數器

   Java程序,每一個線程都有自己的PC寄存器,它是在線程啓動時創建的。大小爲一個字長。線程執行某個方法的時候,PC寄存器總是指向下一條將要被執行的指令的“地址”,“地址”可以是本地指針,或者方法字節碼中相對於該方法起始指令的偏移量。如果線程執行本地方法,PC寄存器的值是“undefined”

 

7.Java棧

   每啓動一個線程,Java虛擬機都會爲它分配一個棧。虛擬機只會對棧進行兩種操作:以幀爲單位的壓棧或出棧。

   當線程調用一個方法的時候,虛擬機都會在該線程的Java棧中壓入一個新幀。當執行這個方法時,它使用這個幀來存儲參數,局部變量,中間運算結果等等數據。

    Java方法有兩種方式返回。一種return,正常返回。一種拋異常終止。不管哪種返回當前幀都會彈出並釋放。上一個方法就成爲當前幀。Java棧上所有數據都是線程私有的。不需要同步。某些實現可以允許指定Java棧的初始大小和最大最小值。

 

8.棧幀

   三部分組成:局部變量區,操作數棧和幀數據區。局部變量區和操作數棧的大小是視方法而定的。它們是按字長計算的。編譯器在編譯時就確定了這些值並放在Class文件中。而幀數據區的大小則依賴於具體的實現。

   局部變量區:被組織成一個以字長爲單位、從0開始計數的數組。字節碼指令通過從0開始的索引使用其中的數據。類型爲int,float,reference和returnAddress的值在其中佔據一項,而double,long則佔據連續的兩項。byte,char,short存入數組前都被轉換爲int值。訪問long和double的時候,指令只需要指出第一項索引的值就可以了。虛擬機是不直接支持boolean的,編譯器用int值來表示boolean。但byte,short,char,java虛擬機是直接支持的,這些類型的值可以作爲實例變量或者數組元素存儲在局部變量區,也可以作爲類變量存儲在方法區中。但在局部變量區和操作數棧中都會轉換成int值,它在棧中是當作int來處理的,只有當它被存回堆或者方法區中時,才轉換成原來的類型。

   操作數棧:和局部變量區一樣,被組織成一個以字長爲單位的數組。但不是用索引來訪問的,是通過標準的棧操作來訪問的,壓棧,出棧。虛擬機在操作數棧中存儲數據的方式和局部變量區一樣。byte,short和char入棧之前都被轉換成int類型。Java虛擬機沒有寄存器,程序計數器也無法被程序指令直接訪問。Java虛擬機的指令是從操作數棧中而不是從寄存器中取得操作數的,因此它的運作方式是基於棧的,而不是基於寄存器的。

    幀數據區:除了局部變量區和操作數棧外,Java棧幀還需要一些數據來支持常量池解析、正常方法返回,異常派發機制。這些信息都保存在幀數據區中。

 

9.本地方法棧

   當某個線程調用本地方法時,它就進入了一個全新的並且不受虛擬機限制的世界。本地方法棧佔用的內存不必是固定的大小,它可以根據需要動態的擴展或者收縮。某些實現也允許用戶或程序員指定本地方法棧的初始大小,最大,最小值等。

 

10.執行引擎

    任何java虛擬機實現的核心都是它的執行引擎。在java虛擬機規範中,執行引擎的行爲使用指令集來定義。對於每條指令,規範詳細定義了當實現執行到該指令時應該處理什麼,但是對如何處理卻言之甚少。實現的設計者有權決定如何執行字節碼,實現可以採取解釋、即時編譯或直接使用芯片上的指令執行,還可以是它們的混合,或者你能想的其他任何新技術。

    運行中Java程序的每一個線程都是一個獨立的虛擬機執行引擎的實例。

    字節碼在class文件中是以字節對齊的。

    指令集:方法的字節碼流是Java虛擬機指令序列構成的。每一條指令包含一個單字節的操作碼,後面跟隨0個或多個操作數。

    線程:Java虛擬機規範定義了線程模型,這個模型的目標是要有助於在很多體系結構上都實現它。任何Java虛擬機的線程實現都必須支持同步的兩個方面:對象鎖,線程等待通知。

Java虛擬機規範中,Java線程的行爲是通過術語----變量、主存和工作內存---來定義的。每一個java虛擬機實例都有一個主存,用於保存所有的程序變量(對象的實例變量、數組的元素以及類變量)。每一個線程都有一個工作內存,線程用它保存所使用和賦值的變量的“工作拷貝”。局部變量和參數,因爲它們是每個線程私有的,可以從邏輯上看成是工作內存或主存的一部分。

 

11.本地方法接口

    並不強求Java虛擬機實現支持任何特定的本地方法接口。

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