《深入理解Java虛擬機器》學習筆記之類檔案結構
1、程式碼編譯的結果:從本地機器碼轉變為位元組碼
-
目的:編譯器將機器碼編譯成位元組碼,特定平臺上的虛擬機器器將位元組碼轉譯為可以直接執行的指令
-
定義解釋:
-
(1)位元組碼(Bytecode):主要為了實現特定軟體執行和軟體環境、與硬體環境無關。
-
位元組碼的實現方式是通過編譯器和虛擬機器器。
-
典型應用:Java Bytecode
-
每一個位元組為8個二進位制數字,有256個可能的程式碼值(2的8次方=256),因此一個位元組的操作碼最多可能有256種不同的操作。 其中,0x00、0xFE、0xCA、0xFF被指定保留
-
-
(2)機器碼(machine code):學名機器語言指令,有時也被稱為原生碼(Native Code),是電腦的CPU可直接解讀的資料。
-
2、越來越多的程式語言選擇了與作業系統和機器指令集無關的、平臺中立的格式作為程式編譯後的儲存格式
二、無關性的基石(虛擬機器和位元組碼儲存格式是實現語言無關性的基礎)
1、初步實現
-
位置:作業系統的應用層
-
方式:Sun公司以及其他虛擬機器提供商釋出了許多可以執行在各種不同平臺上的虛擬機器,這些虛擬機器都可以載入和執行同一種平臺無關的位元組碼,從而實現了“一次編寫,到處執行”
-
流程:各種語言的程式 -> javac編譯器 -> 位元組碼(*.class)-> Java虛擬機器
2、理論基礎
-
特點:Java虛擬機器不和包括Java在內的任何語言繫結,它只與“Class檔案”這種特定的二進位制檔案格式所關聯。
- Class檔案中包含了Java虛擬集指令集和符號表以及若干其他輔助資訊
-
安全:基於安全方面的考慮,Java虛擬機器規範要求在Class檔案中使用許多強制性的語法和結構化約束,但任意一門功能性語言都可以表示為一個能被Java虛擬機器所接受的有效Class檔案。
- 作為一個通用的、機器無關的執行平臺,任何其他語言的實現者都可以將Java虛擬機器做為語言的產品交付媒介
三、Class類檔案的結構
- 任何一個Class檔案都對應著唯一一個類或介面的定義資訊,但類或介面不一定都得定義在檔案裡(不一定以磁碟檔案格式存在)
- eg:類或介面也可以通過類載入器直接生成
0、基本理解
-
Class檔案是一組以8位位元組為基礎單位的二進位制流
-
各個資料專案嚴格按照順序緊湊地排列在Class檔案之中,中間沒有新增任何分隔符
- 因此Class檔案中儲存的內容幾乎全部是程式執行的必要資料,沒有空隙存在
-
當遇到需要佔用8位位元組以上空間的資料時,則會按照高位在前的方式分割成若干個8位位元組進行儲存
- 這種排序稱為“Big-Endian”:具體是指最高位位元組在地址最低位、最低位位元組在地址最高位的順序來儲存資料。而x86等處理器使用的是相反的“Little-Endian”
-
-
Class檔案格式採用一種類似與C語言結構體的偽結構來儲存資料,只有兩種資料型別:無符號數、表
-
(1)無符號數:基本的資料型別
-
以u1/u2/u4/u8來分別代表1個位元組、2個位元組、4個位元組和8個位元組的無符號數
-
無符號數可以用來描述數字、索引引用、數量值或者按照UTF-8編碼構成的字串值
-
-
(2)表:由多個無符號數或者其他表作為資料項構成的複合型別資料
-
表用於描述有層次關係的複合結構的資料
-
所有表都習慣性的以“_info”結尾
-
-
某一型別的集合:無論是無符號數還是表,當需要描述同一個型別但數量不定的多個數據時,經常會使用一個前置的容量計數器加若干個連續的資料項的形式,這一系列的連續的某一型別的資料為某一型別的集合
-
-
Class檔案格式
型別 名稱 數量 註解 u4 magic 1 魔數 u2 minor_version 1 Class檔案次版本號 u2 major_version 1 Class檔案主版本號 u2 constant_pool_count 1 常量池容量計數值 cp_info constant_pool constant_pool_count - 1 常量表 u2 access_flags 1 訪問標誌 u2 this_class 1 類索引 u2 super_class 1 父類索引 u2 interfaces_count 1 介面計數器 u2 interfaces interfaces_count 介面索引集合 u2 fields_count 1 欄位表容量計數器 field_info fields fields_count 欄位表 u2 methods_count 1 方法計數器 method_info methods methods_count 方法表 u2 attributes_count 1 屬性計數器 attribute_info attributes attributes_count 屬性表
1、魔數與Class檔案的版本
-
(1)魔數:
-
位置:每個Class檔案的第1-4個位元組
-
作用:唯一作用是確定這個檔案是否為一個能被虛擬機器接收的Class檔案(用於身份識別)
-
出現原因:基於安全方面的考慮,使用魔數而不是易於改動的副檔名
-
內容:
0xCAFEBABE
(咖啡寶貝?)
-
-
(2)Class檔案的版本號
-
次版本號(Minor Version)
- 位置:第5-6個位元組
-
主版本號(Major Version)
- 位置;第7-8個位元組
-
Java的版本號(主)從45開始,從JDK1.1開始每次大版本升級版本號+1
- eg:JDK1.7支援到最大版本號為51
- 版本支援向下相容
-
2、常量池
-
(1)常量池容量計數值(constant_pool_count)
-
位置:第9-10個位元組,常量池之前,主版本號之後
-
特點:
-
容量計數是從1開始的
-
第0項常量空出來:用於滿足後面某些指向常量池的索引值的資料在特定情況下需要表達“不引用任何一個常量池專案”的含義,這種情況可以將索引值置為0來表示
-
-
-
(2)常量池(constant_pool)
-
位置:常量池容量計數值之後
constant_pool_count - 1
個位元組 -
存放兩大類內容:字面量、符號引用
-
1⃣字面量:接近於Java語言層面的常量,如文字字串,宣告為final的常量值等
-
2⃣符號引用:編譯原理方面的概念。用於在虛擬機器載入Class檔案動態連結時,在常量池中獲取符號引用以獲取對應方法、欄位的最終記憶體佈局資訊
- a:類和介面的全限定名
- b:欄位的名稱和描述符
- c:方法的名稱和描述符
-
-
常量池中每一項常量都是一個表(目前有14種)
-
共同點:14種表開始的第一位都是一個u1型別(1位元組無符號數)的標誌位(tag),代表當前這個常量屬於哪種常量型別
-
不同點:14種常量型別各自均有自己的結構
-
-
為什麼一個Class檔案中欄位名、方法最大隻能是64KB?
-
因為Class檔案中的方法欄位等都需要引用
CONSTANT_Utf8_info
型常量來描述名稱CONSTANT_Utf8_info
型常量的結構
型別 名稱 數量 u1 tag 1 u2 length 1 u1 bytes length - length值說明了這個UTF—8編碼的字串長度是多少位元組
-
所以
CONSTANT_Utf8_info
型常量的最大長度也就是Java中方法、欄位名的最大長度- 最大長度就是lenth的最大值,即u2型別所能表達的最大值65535(2的十六次方-1),即64KB
- 有變數名或方法超過這個長度將無法編譯
-
3、訪問標誌
-
位置:常量池結束之後的兩個位元組
-
作用:用於識別一些類或者介面層次的訪問資訊
-
內容:最終的值是各個標誌值的和
-
注意:access_flags中一共有16個標誌位可以使用
- 當前只定義了其中8個,沒有使用到的標誌位要求一律為0
標誌名稱 | 標誌值 | 含義 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否為public型別 |
ACC_FINAL | 0x0010 | 是否被宣告為final,只有類可設定 |
ACC_SUPER | 0x0020 | 是否允許使用invokespecial位元組碼指令的新語意。此位元組碼指令的語義在JDK1.0.2中發生過改變,為了區別這條指令是否使用新語義,JDK1.0.2之後編譯出來的類這個標誌都要為真 |
ACC_INTERFACE | 0x0200 | 標識這是一個介面 |
ACC_ABSTRACT | 0x0400 | 是否為abstract型別,對於介面或者抽象類來說,此標誌值為真,其他類值為假 |
ACC_SYNTHETIC | 0x1000 | 標識這個類並非由使用者程式碼產生的 |
ACC_ANNOTATION | 0x2000 | 標識這是一個註解 |
ACC_ENUM | 0x4000 | 標識這是一個列舉 |
4、類索引、父類索引與介面索引集合(用於確定類的繼承關係)
-
位置:依次排列於訪問標誌之後
-
型別:類索引和父類索引都是u2型別的資料,介面索引集合是一組u2型別的資料集合
-
作用:
- 類索引:確定這個類的全限定名
- 父類索引:確定這個類的父類的全限定名
- 介面索引集合:描述這個類實現了哪些介面
-
特徵:
-
類索引、父類索引:其各自指向一個型別為
CONSTANT_Class_info
型別的類描述符常量,通過CONSTANT_Class_info
型別的常量中的索引值可以找到定義在CONSTANT_Utf8_info
型別常量中的全限定名字串 -
介面索引集合:入口的第一項為一個u2型別的介面計數器,表示索引表的容量
- 如果該類沒有實現任何介面,則該計數器的值為0,後面介面的索引表不再佔用任何位元組
-
5、欄位表集合
-
欄位表容量計數器(fields_count)
- 位置:介面索引集合之後,欄位表之前
- 型別:u2
-
定義:
- 欄位表(field_info):用於描述介面或者類中宣告的變數
- 欄位(field):包括類級變數以及例項級變數,但不包括方法內部宣告的區域性變數
-
欄位表結構:
型別 名稱 數量 註解 u2 access_flags 1 欄位訪問標誌 u2 name_index 1 欄位的簡單名稱 u2 descriptor_index 1 欄位和方法的描述符 u2 attributes_count 1 屬性表計數器 attribute_info attributes attributes_count 儲存額外資訊的屬性表 -
欄位訪問標誌(access_flags)結構:
標誌名稱 標誌值 含義 ACC_PUBLIC 0x0001 欄位是否public ACC_PRIVATE 0x0002 欄位是否private ACC_PROTECTED 0x0004 欄位是否protected ACC_STATIC 0x0008 欄位是否static ACC_FINAL 0x0010 欄位是否final ACC_VOLATILE 0x0040 欄位是否欄位是否volatile(併發可見性,是否強制從主記憶體讀寫) ACC_TRANSIENT 0x0080 欄位是否transient(可否被序列化) ACC_SYNTHETIC 0x1000 欄位是否由編輯器自動產生的 ACC_ENUM 0x4000 欄位是否enum -
全限定名、簡單名稱、方法和欄位描述符區別
-
全限定名:將完整類名(含包名)中的 . 替換為 /
- eg:java/lang/Object
-
簡單名稱:沒有型別和引數修飾的方法或欄位名稱
- eg:inc()方法和欄位m的簡單名稱就是inc和m
-
方法和欄位的描述符:用於描述欄位的資料型別、方法的引數列表(包括數量、型別以及順序)和返回值
-
-
描述符的查詢流程
- 根據描述符的值(數字)到常量池中尋找對應的字串(字母)從而還原始碼資訊
-
描述符規則:
-
(1)基本資料型別以及無返回值的void型別:都用一個規定好的大寫字元表示(有標準表可查詢)
-
(2)物件型別:用字元L加物件的全限定名來表示
- eg;Ljava/lang/Object
-
(3)陣列型別;每一個維度將使用一個前置的 “ [ ” 來描述
- eg:String型別的二維陣列表示為 “[[Ljava/lang/String”
- eg: int型別的一維陣列可表示為 “[I”
-
(4)方法:先引數列表,後返回值的順序描述
- eg:String型別的方法 “()Ljava/lang/String”
-
-
儲存額外資訊的屬性表(attributes)
- eg:
fianl static int m = 123
,可能會存在一項ConstantValue屬性,指向常量123
- eg:
-
特別注意:
-
欄位表集合中不會列出從超類或父介面中繼承來的欄位,但可能列出如內部類持有的外部類引用等欄位
-
Java語言中欄位不能過載,但對於位元組碼來講,如果兩個欄位的描述符不一致,那欄位重名就是合法的
-
6、方法表集合
-
結構:與欄位表一致
- 方法訪問標誌也基本與欄位表的資訊一致
-
方法中的Java程式碼去哪了?
- 方法體中的程式碼經過編譯器編譯程位元組碼指令後,存放在方法屬性表集合中一個名為“Code”的屬性裡面
- 屬性表是Class檔案格式中最具擴充套件性的一種資料專案
- 方法體中的程式碼經過編譯器編譯程位元組碼指令後,存放在方法屬性表集合中一個名為“Code”的屬性裡面
-
特徵簽名:一個方法中各個引數在常量池中的欄位符號引用的集合(Java中體現為引數的型別、順序和數量)
- Class檔案中為只要描述符不完全一致也可以共存
- 因此如果兩個方法有相同的名稱和特徵簽名,但返回值不同,那麼也是可以合法共存於同一個Class檔案中的
7、屬性表集合
-
定義:在Class檔案、欄位表、方法表都可以攜帶自己的屬性表集合,用以描述某些場景專有的資訊
-
特點:不再要求各個屬性表具有嚴格的順序,並且只要不與已有屬性名重複,任何人實現的編譯器都可以向屬性表中寫入自己定義的屬性資訊,Java虛擬機器執行時會忽略掉它不認識的屬性(虛擬機器規範中有預定義屬性)
-
一個符合規則的屬性表結構
型別 名稱 數量 u2 attribute_name_index 1 u4 attribute_lenth 1 (可變) info attribute_length - 對於每個屬性,他的名稱需要從常量池中引用一個CONSTANT_Utf8_info型別的常量來表示
- 屬性值的結構是完全自定義的
- 需要通過一個u4長度的屬性去說明屬性值所佔用的位數
-
(1)Code屬性
-
定義:Java程式方法體重的程式碼經過Javac編譯器處理後,最終變為位元組碼指令儲存在Code屬性內。
- Code屬性出現在方法表的屬性集合之中
-
Slot:是虛擬機器為區域性變數分配記憶體所使用的最小單位
- 區域性變量表中的Slot可以重用
型別 大小 byte、char、float、int、short、boolean和returnAddress等長度不超過32位的資料型別 1個Slot double、long這兩種64位資料型別 2個Slot
-
-
(2)Exceptions屬性
-
定義:列舉出方法中可能丟擲的受查異常(Checked Excepitons),也就是方法描述時在throws關鍵字後面列舉的異常
-
結構
型別 名稱 數量 u2 attribute_name_index 1 u4 attribute_length 1 u2 number_of_exceptions 1 u2 exception_index_table number_of_exceptions - 每一種受查異常使用一個
exception_index_table
項表示,exception_index_table
是一個指向常量池中CONSTANT_Class_info
型常量的索引,代表了該受查異常的型別
- 每一種受查異常使用一個
-
-
(3)LineNumberTable屬性
- 定義:用於描述Java原始碼行號與位元組碼行號(位元組碼的偏移量)之間的對應關係
- 並不是執行時必需的屬性,但預設會生成到Class檔案之中
- 定義:用於描述Java原始碼行號與位元組碼行號(位元組碼的偏移量)之間的對應關係
-
(4)LocalVariableTable屬性
- 定義:LocalVariableTable屬性用於描述棧幀中區域性變量表中的變數與Java原始碼中定義的變數之間的關係
- 並不是執行時必須的屬性,但預設會生成到Class檔案之中
- 如果沒有這項屬性,最大的影響就是當其他人引用這個方法時,所有的引數名稱都將會丟失,IDE會使用arg0等佔位符代替原有引數名。對程式執行沒有影響,但是在除錯期間無法根據引數名稱從上下文中獲得引數值
- 定義:LocalVariableTable屬性用於描述棧幀中區域性變量表中的變數與Java原始碼中定義的變數之間的關係
-
(5)SourceFile屬性
- 定義:用於記錄生成這個Class檔案的原始碼檔名稱
-
(6)ConstantValue屬性
-
定義:用於通知虛擬機器自動為靜態變數賦值,只有被
static
關鍵字修飾的變數(類變數)才可以使用這項屬性 -
1⃣對於非static型別的變數(例項變數)的賦值:在例項構造器的
<init>
方法中進行 -
2⃣對於類變數:兩種方式
初始化方式 目前javac編譯器的選擇使用該方式的情況 生成ConstantValue屬性來初始化 同時使用 final static
修飾,且該變數的資料型別為基本資料型別或字串(String)類構造器 <client>
方法初始化非final或非基本資料型別/字串
-
-
(7)InnerClasses屬性
- 定義:用於記錄內部類與宿主類之間的關聯
-
(8)Deprecated及Synthetic屬性
-
特徵:都屬於標誌型別的布林屬性,只存在有和沒有的區別,沒有屬性值的概念
-
定義:
-
Deprecated:用於表示某個類、欄位或者方法(不再推薦使用,可在程式碼中通過
@Deprecated
註解進行設定) -
Synthetic:代表此欄位或者方法並不是由Java原始碼直接產生的,而是由編譯器自行新增的
-
-
JDK1.5之後,標識一個類、欄位或者方法是編譯器自動產生的,也可以設定他們的訪問標識中的
ACC_SYNTHETIC
標誌位- eg:Bridge Method
-
所有由非使用者程式碼產生的類、方法及欄位都應當至少設定Synthetic屬性和ACC_SYNTHETIC標誌位中的一項,唯一例外的是例項構造器
<init>
方法和類構造器<client>
方法
-
-
(9)StackMapTable屬性(位元組碼驗證)
-
定義:會在虛擬機器類載入的位元組碼驗證階段,被新型別檢查驗證器使用,目的在於代替以前比較消耗效能的基於資料流分析的型別推導驗證器
- 新的驗證器在同樣能保證Class檔案合法性的前提下,省略了在執行期通過資料流分析去確認位元組碼的行為邏輯合法性的步驟,而是在編譯階段將一系列的驗證型別直接記錄在Class檔案之中,通過檢查這些驗證型別代替了型別推導過程,從而大幅提升了位元組碼驗證的效能
-
特點;
- 變長
- 位於Code屬性的屬性表中
- 一個方法的Code屬性最多隻能有一個StackMapTable屬性,否則將丟擲
ClassFormatError
異常
-
驗證方式
- StackMapTable屬性中包含零至多個棧對映幀,每個棧對映幀都顯式或隱式地代表了一個位元組碼偏移量,用於表示該執行到該位元組碼時,區域性變量表和運算元棧的驗證型別
-
型別檢查驗證器會通過檢查目標方法的區域性變數和運算元棧所需要的型別來確定一段位元組碼指令是否符合邏輯約束
-
-
(10)Signature屬性(記錄泛型型別)
-
定義:任何類、介面、初始化方法或成員的泛型簽名如果包含了型別變數或引數化型別,則Signature屬性會為它記錄泛型簽名信息
-
出現原因:Java語言的泛型採用的是擦除法實現的偽泛型,在位元組碼(Code屬性)中,泛型資訊編譯之後型別變數、引數化型別都會被擦除掉
-
型別擦除的好處:實現簡單、分廠容易實現Backport,執行期能夠節省一些型別所佔的記憶體空間
-
型別擦除的壞處:執行期無法將泛型型別與使用者定義的普通型別同等對待,如無法進行執行期反射獲取泛型資訊(目前Java的反射API能夠獲取泛型型別,就是因為這個引數的存在)
-
-
-
(11)BootstrapMethods屬性
-
定義:用於儲存invokedynamic指令引用的引導方法限定符
-
特點:
- 變長
- 位於類檔案的屬性表中
- 類檔案的屬性表中最多也只能又一個BootstrapMethods屬性
-
四、位元組碼指令簡介
-
Java虛擬機器的指令由一個位元組長度的、代表著某種特定操作含義的數字(稱為操作碼,Opcode)以及跟隨其後的零至多個代表此操作所需引數(稱為運算元,Operands)而構成
-
位元組碼指令集優缺點
-
缺點:
-
1⃣指令集的操作碼總數不可能超過256條(原因:限制了Java虛擬機器操作碼的長度為1個位元組,即0-255)
-
2⃣解釋執行位元組碼時會損失效能(原因:由於Class檔案格式放棄了編譯後代碼的運算元長度對齊,這就意味著虛擬機器處理那些超過一個位元組資料的時候,不得不在執行時從位元組中重建出具體資料的結構)
-
-
優點;
-
1⃣放棄了運算元對齊,就意味著可以省略很多填充和間隔符號
-
2⃣用一個位元組來代表操作碼,也是為了儘可能獲得短小精幹的編譯程式碼(儘可能小資料量、高傳輸效率的設計是由Java語言設計之初面向網路、智慧家電的技術背景所決定的)
-
-
-
Java虛擬機器的直譯器的基本執行模型(不考慮異常處理)
do { 自動計算PC暫存器的值加1; 根據PC暫存器的指示位置,從位元組碼流中取出操作碼; if (位元組碼存在運算元) 從位元組碼流中取出運算元; 執行操作碼所定義的操作; } while(位元組碼流長度 > 0)
1、位元組碼與資料型別
-
在Java虛擬機器的指令集中,大多數指令都包含了其操作所對應的資料型別資訊
-
eg:iload、fload分別代表int操作和float操作
-
注:這兩條指令的操作在虛擬機器內部可能會是由同一段程式碼來實現的,但在Class檔案中它們必須擁有各自獨立的操作碼
-
-
對於大部分的資料型別相關的位元組碼指令,它們的操作碼助記符中都由特殊的字元來表明專門為哪種資料型別服務
字元 型別 i int l long s short b byte c char f float d double a reference arraylenth 陣列 goto 與資料型別無關 -
由於Java虛擬機器的操作碼長度只有一個位元組,Java虛擬機器的指令集對於特定的操作只提供了有限的型別相關指令去支援它(並非每種資料型別和每一種操作都有對應的指令)。有一些單獨的指令可以在必要的時候用來將一些不支援的型別轉換為可被支援的型別。
-
查表6-31可知,通過使用資料型別列所代表的特殊字元替換opcode列的指令模版中的T,就可以得到一個具體的位元組碼指令
-
eg:沒有指令支援boolean型別。
- 大多數對於boolean、byte、short和char型別資料的操作,實際上都是使用相應的int型別作為運算型別的
-
2、位元組碼操作按用途分為9類
-
(1)載入和儲存指令
-
作用:用於將資料在棧幀中的區域性變量表和運算元棧之間來回傳輸
-
iload_<n>
中的<n>
是代表了一組指令
-
-
(2)運算指令
-
作用:用於對兩個運算元棧上的值進行某種特定的運算,並把結果重新存入到操作棧頂
-
分類:大體上算術指令可以分為兩種
- 對整型資料進行運算的指令
- 對浮點型資料進行運算的指令
-
如果某個操作結果沒有明確的數學定義的話,將會使用
NaN
值來表示
-
-
(3)型別轉換指令
-
作用:可以將兩種不同的數值型別進行相互轉換,這些轉換操作一般用於實現使用者程式碼中顯式型別轉換操作,或者用來處理位元組碼指令集中資料型別相關指令無法與資料型別一一對應的問題
-
窄化型別轉換:存在上限溢位、下限溢位和精度丟失等情況
-
-
(4)物件建立與訪問指令
- 作用:Java虛擬機器對類例項和陣列的建立與操作使用了不同的位元組碼指令。物件建立後,就可以通過物件訪問指令獲取物件例項或者陣列例項中的欄位或者陣列元素
-
(5)運算元棧管理指令
- 作用:直接操作運算元棧
-
(6)控制轉移指令
- 作用:可以讓Java虛擬機器有條件或無條件的從指定的位置指令而不是控制轉移指令的下一條指令繼續執行程式
- 從概念模型上理解,可以認為控制轉移指令就是在有條件或無條件的修改PC暫存器的值
- 作用:可以讓Java虛擬機器有條件或無條件的從指定的位置指令而不是控制轉移指令的下一條指令繼續執行程式
-
(7)方法呼叫和返回指令
- 作用:呼叫相關物件、類、介面方法(與資料型別無關),根據返回值型別呼叫方法返回指令
-
(8)異常處理指令
- 作用:顯式丟擲異常(throw語句)都由
athrow
指令來實現
- 作用:顯式丟擲異常(throw語句)都由
-
(9)同步指令
-
作用:Java虛擬機器可以支援方法級的同步和方法內部一段指令序列的同步,這兩種同步都是使用 管程(Monitor) 來支援的
-
1⃣方法級同步:方法級同步是隱式的,即無需通過位元組碼指令來控制,它實現在方法呼叫和返回操作之中
-
虛擬機器可以從方法常量池的方法表結構中的
ACC_SYNCHRONIZED
訪問標誌得知一個方法是否宣告為同步方法 -
當方法呼叫時,呼叫指令將會檢查方法的
ACC_SYNCHRONIZED
訪問標誌是否被設定 -
如果設定了,執行執行緒就被要求先成功持有管程,然後才能執行方法,最後當方法完成(無論是正常完成還是非正常完成)時釋放管程
-
在方法執行期間,執行執行緒持有了管程,其他任何執行緒都無法再獲取到同一個管程
-
如果一個方法執行期間丟擲了異常,並且在方法內部無法處理此異常,那麼這個同步方法所持有的管程將在異常拋到同步方法之外時自動釋放
-
-
2⃣同步一段指令集序列:通常是由Java語言中的
synchronized
語句塊來表示的- Java虛擬機器的指令集中有
monitorenter
和monitorexit
兩條指令來支援synchronized
關鍵字的語義
- Java虛擬機器的指令集中有
-
五、公有設計和私有實現
-
Java虛擬機器規範描繪了Java虛擬機器應用的共同程式儲存格式:Class檔案格式和位元組碼指令集
- 這些內容與硬體、作業系統及具體的Java虛擬機器實現之間是完全獨立的
-
虛擬機器在外部介面合乎規範的情況下可以有多種實現
- 虛擬機器實現者可以使用這種伸縮性來讓Java虛擬機器獲得更高的效能、更低的記憶體消耗或者更好的可移植性
-
目前虛擬機器實現的兩種主要方式
-
(1)將輸入的Java虛擬機器程式碼在載入或執行時翻譯成另外一種虛擬機器的指令集
-
(2)將輸入的Java虛擬機器程式碼在載入或執行時翻譯成宿主機CPU的本地指令集(即JIT程式碼生成技術)
-
六、Class檔案結構的發展
- Class檔案格式所具備的平臺中立(不依賴於特定硬體及作業系統)、緊湊、穩定和可擴充套件的特點,是Java計數體系實現平臺無關、語言無關兩項特性的重要支柱