小型網(wǎng)站開發(fā)用什么語言營銷案例網(wǎng)站
GitHub - Omooo/Android-Notes: ???這有一包小魚干,確定不要吃嘛?( 逃
深入理解Android:Java虛擬機ART 讀書筆記 以下內容均來自書中內容 建議看原書哦
第2章 深入理解Class文件格式
2.1 class文件總覽
Class文件格式全貌 u4:表示這個域長度為4個字節(jié),內容為無符號整數(shù) u2:表示這個域長度為2個字節(jié),內容為無符號整數(shù)
Class文件前8個字節(jié)依次是magic(4個字節(jié)長,取值必須是0xCAFEBABE)、minor_version(2個字節(jié)長,表示該class文件版本的小版本信息)和major_verion(2個字節(jié)長,表示該class文件版本的大版本信息)。
constant_pool_count表示常量池數(shù)組中元素的個數(shù),而constant_pool是一個存儲cp_info信息(cp為constant pool縮寫,譯為常量池)的數(shù)組。每一個Class文件都包含一個常量池。常量池在代碼中對應為一個數(shù)組,其元素的類型就是cp_info。注意,cp數(shù)組的索引從1開始。
access_flags:標明該類的訪問權限,比如public、private之類的信息。
this_class和super_class:存儲的是指向常量池數(shù)組元素的索引。通過這兩個索引和常量池對應元素的內容,我們可以知道本類和父類的類名(只是類名,不包含包名。類名最終用字符串描述)。
interfaces_count和interfaces:這兩個成員表示該類實現(xiàn)了多少個接口以及接口類的類名。和this_class一樣,這兩個成員也只是常量池數(shù)組里的索引號。真正的信息需要通過解析常量池的內容才能得到。
fields_count和fields:該類包含了成員變量的數(shù)量和它們的信息。成員變量信息由field_info結構體表示。
methods_count和methods:該類包含了成員函數(shù)的數(shù)量和它們的信息。成員函數(shù)信息由method_info結構體表示。
attributes_count和attributes:該類包含的屬性信息。
2.2 常量池及相關內容
常量池的英文叫Constant Pool,對應的數(shù)據(jù)結構偽代碼就是一個類型為cp_info的數(shù)組。每一個cp_info對象存儲了一個常量項。cp_info對應數(shù)據(jù)結構的偽代碼如下所示。
CONSTANT_String和CONSTANT_Utf8的區(qū)別
CONSTANT_Utf8:該常量項真正存儲了字符串的內容。以后我們將看到此類型常量項對應的數(shù)據(jù)結構中有一個字節(jié)數(shù)組,字符串就存儲在這個字節(jié)數(shù)組中。
CONSTANT_String:代表了一個字符串,但是它本身不包含字符串的內容,而僅僅包含一個指向類型為CONSTANT_Utf8常量項的索引。
信息描述規(guī)則:
原始數(shù)據(jù)類型對應的字符串描述為"B""C""D""F""I""J""S""Z",它們分別對應的Java類型為byte、char、double、float、int、long、short、boolean。
引用數(shù)據(jù)類型的格式為"LClassName;"。此處的ClassName為對應類的全路徑名,比如上例中的"Ljava/lang/String;"。全路徑名的“.”號由“/”替代,并且最后必須帶分號。
數(shù)組也是一種引用類型,數(shù)組用"[其他類型的描述名"來表示,比如一個int數(shù)組的描述為"[I",一個字符串數(shù)組的描述為"[Ljava/lang/String;",一個二維int數(shù)組的描述為"[[I"
https://github.com/Omooo/Android-Notes/blob/master/blogs/JVM/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3%20Class%20%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F/%E5%B8%B8%E9%87%8F%E6%B1%A0%E5%8F%8A%E7%9B%B8%E5%85%B3%E5%86%85%E5%AE%B9.md
javap-verbose class文件名 查看Class代碼
2.5 屬性介紹
attribute_info { u2 attribute_name_index; // 屬性名稱,指向常量池中Utf8常量項的索引 u4 attribute_length; // 該屬性具體內容的長度,即下面info數(shù)組的長度 u1 info[attribute_length]; // 屬性具體內容 }
和常量池類型不一樣的是,屬性是由其名稱來區(qū)別的,即attribute_info中的attribute_name_index,所指向的Utf8字符串。
虛擬機在解析Class文件的時候是需要校驗很多內容的,比如abstract的函數(shù)或native的函數(shù)就不能攜帶"Code"屬性。
Code屬性:
attribute_name_index指向內容為"Code"的Utf8_info常量項。attribute_length表示接下來內容的長度。
max_stack:JVM執(zhí)行一個指令的時候,該指令的操作數(shù)存儲在一個名叫“操作數(shù)棧(operandstack)”的地方,每一個操作數(shù)占用一個或兩個(long、double類型的操作數(shù))棧項。stack就是一塊只能進行先入后出的內存。max_stack用于說明這個函數(shù)在執(zhí)行過程中,需要最深多少??臻g(也就是多少棧項)。max_locals表示該函數(shù)包括最多幾個局部變量。注意,max_stack和max_locals都和JVM如何執(zhí)行一個函數(shù)有關。根據(jù)JVM官方規(guī)范,每一個函數(shù)執(zhí)行的時候都會分配一個操作數(shù)棧和局部變量數(shù)組。所以Code_attribute需要包含這些內容,這樣JVM在執(zhí)行函數(shù)前就可以分配相應的空間。
code_length和code:函數(shù)對應的指令內容也就是這個函數(shù)的源碼經過編譯器轉換后得到的Java指令碼存儲在code數(shù)組中,其長度由code_length表明。
exception_table_length和exception_table:一個函數(shù)可以包含多個try/catch語句,一個try/catch語句對應exception_table數(shù)組中的一項。
介紹exception_table之前需要先了解pc(program counter)的概念。JVM執(zhí)行的時候,會維護一個變量來指向當前要執(zhí)行的指令,這個變量就叫pc。有了pc的概念,exception_table中各成員變量的含義就比較容易理解了。其中:
- start_pc描述try/cath語句從哪條指令開始。注意,這個table中的各個pc變量的取值必須位于代表整個函數(shù)內容的Java字節(jié)碼code[code_length]數(shù)組中。
- end_pc表示這個try語句到哪條指令結束。注意,只包括try語句,不包括catch。
- handler_pc表示catch語句的內容從哪條指令開始。
- catch_type表示catch中截獲的Exception或Error的名字,指向Utf8_info常量項。如果catch_type取值為0,則表示它是final{}語句塊。
Code_attribute里常見的屬性有:
- LineNumberTable用于調試,比如指明哪條指令。對應于源碼哪一行。
- LocalVariableTable用于調試,調試時可以用于計算本地變量的值。
- LocalVariableTypeTable,功能和LocalVariableTable類似。
- StackMapTable為Java 1.6以上才支持的屬性。JVM加載Class文件的時候,將利用該屬性的內容對函數(shù)進行類型校驗(Type Checking)。
LineNumberTable屬性:
start_pc:指向Code_attribute中code數(shù)組某處指令。
line_number:說明start_pc位于源碼的哪一行。注意,多個line_number_table元素可以指向同一行代碼。因為一行Java代碼很可能編譯成多條指令。
LineNumberTable的line 7:0表示code數(shù)組里第1個指令碼(即code[0])來自源碼的第7行。
code[0]解析后得到是new指令。
查看左邊的源代碼可知,第7行確實對應的是一個new操作。
start_pc和length這兩個參數(shù)決定了一個局部變量在code數(shù)組中的有效范圍。
name_index:此局部變量的名字,指向Utf8_info常量項。
descriptor_index:此局部變量的類型,也指向Utf8_info常量項,其內容是Field Descriptor字符串描述。
2.6 Java指令碼介紹
根據(jù)前述知識,我們知道Code_attribute中的code數(shù)組存儲了一個函數(shù)源碼經過編譯后得到的Java字節(jié)碼。根據(jù)Java虛擬機規(guī)范,code數(shù)組只能包括下面兩種類型的信息:
首先是Java指令碼,即指示JVM該做什么動作,比如是進行加操作還是減操作,或者是new一個對象。在JVM規(guī)范中,指令碼的長度是1個字節(jié)。所以JVM規(guī)范中定義的Java指令碼的個數(shù)不會超過255個(255的16進制表示為0xFF)。
緊接指令碼之后的是0個或多個操作數(shù)。JVM在執(zhí)行某條Java指令碼的時候,往往還需要其他一些參數(shù)。參數(shù)可以直接存儲在code數(shù)組里,也可以存儲在操作棧(Operand stack)中。由于不同指令碼可能需要不同個數(shù)的參數(shù),所以指令碼后面的內容可以是參數(shù)(如果這條指令碼需要參數(shù))也可以是下一條指令(如果這條指令碼無需參數(shù))。
invokevirtual指令
對invokevirtual來說,該指令后面需要兩個字節(jié)的參數(shù)。大多數(shù)指令執(zhí)行的時候,JVM需要預先準備好操作數(shù)棧,里邊存儲了該指令需要的信息。
復制一份復制棧頂元素。將新復制的值插入操作數(shù)棧,但是位置不在棧頂,而是離當前棧頂元素之后的兩個位置。
JAVA VM官方規(guī)范The Java? Virtual Machine Specification
invokedynamic的簡單介紹 http://www.javaworld.com/article/2860079/scripting-jvm-languages/invokedynamic-101.html
todo:自己用JAVA完成一個class文件解析的程序