java解析dxf文件_浅析JVM方法解析、创建和链接
一:前言
上周末寫(xiě)了一篇文章《你知道Java類(lèi)是如何被加載的嗎?》,分析了HotSpot是如何加載Java類(lèi)的,干脆趁熱打鐵,本周末再來(lái)分析下Hotspot又是如何解析、創(chuàng)建和鏈接類(lèi)方法的。
二:Class文件中的Java方法
Java類(lèi)在編譯后會(huì)被編譯成 Class 文件,在幾年前寫(xiě)的《Jvm之用java解析class文件》中,我對(duì) Class 文件的結(jié)構(gòu)進(jìn)行了分析,里面已經(jīng)講過(guò)了Java 方法在 Class 文件中的結(jié)構(gòu),今天就再溫故而知新下。
先來(lái)看下 Class 文件的結(jié)構(gòu):
ClassFile- methods_count 記錄了 Class 文件中一共有多少方法。
- methods 是個(gè)數(shù)組,包含 Class 文件的所有方法。
methods 的數(shù)組類(lèi)型為 method_info,每個(gè) method_info 對(duì)應(yīng)一個(gè) Java 方法。
method_info {u2 access_flags;u2 name_index;u2 descriptor_index;u2 attributes_count;attribute_info attributes[attributes_count]; }- access_flags 是方法的訪問(wèn)權(quán)限。
- name_index 是方法名在常量池中的索引。
- descriptor_index 是方法描述符在常量池中的索引。
- attributes_count 記錄了方法一共有多少屬性。
- attributes是個(gè)數(shù)組,包含了方法的所有屬性。
attributes 中的每一項(xiàng)都是方法的一個(gè)屬性,其中代表字節(jié)碼的屬性為 Code_attribute:
Code_attribute {u2 attribute_name_index; u4 attribute_length;u2 max_stack;u2 max_locals;u4 code_length;u1 code[code_length];u2 exception_table_length; {u2 start_pc;u2 end_pc;u2 handler_pc;u2 catch_type;} exception_table[exception_table_length];u2 attributes_count;attribute_info attributes[attributes_count]; }- max_stack 表示當(dāng)前方法操作數(shù)棧的最大深度。
- max_locals 表示當(dāng)前方法局部變量的最大個(gè)數(shù)。
- code[code_length] 記錄了方法中的字節(jié)碼指令
總的來(lái)說(shuō),Class 文件中對(duì)方法的描述還是很簡(jiǎn)潔清晰的。
三:HotSpot 如何解析 Java 方法
Class 文件相當(dāng)于 Java 類(lèi)的模板,JVM 在讀取 Class 文件后,會(huì)根據(jù)這個(gè)模板,建立 Java 類(lèi)在虛擬機(jī)中的模型。
在上篇文章《你知道Java類(lèi)是如何被加載的嗎?》中,我提到了 ClassFileParser,它是HotSpot 加載類(lèi)所需要的一員大將,通過(guò)名字我們就能猜出它的作用:類(lèi)文件解析器。
還記得Class在JVM中對(duì)應(yīng)的 InstanceKlass 是如何創(chuàng)建的嗎?不記得話(huà)看一下下面這段代碼來(lái)回憶下。
ClassFileParser上面這段代碼主要是創(chuàng)建了一個(gè)ClassFileParser,并調(diào)用了其create_instance_klass()來(lái)創(chuàng)建 InstanceKlass。但是對(duì)于Class文件的解析,是在 create_instance_klass()之前就完成了的。當(dāng)經(jīng)過(guò)一系列初始化操作后,ClassFileParser 便在其構(gòu)造函數(shù)的末尾,調(diào)用 parse_stream(stream, CHECK),開(kāi)始了 Class 文件的解析之旅。
在 parse_stream()中,ClassFileParser 會(huì)對(duì)整個(gè)Class文件解析解析,包括常量池、字段、父類(lèi)、接口等信息,當(dāng)然也包括類(lèi)方法。用來(lái)解析所有類(lèi)方法的函數(shù)為:parse_methods()。
voidClassFileParser 對(duì)于 Class文件的解析是流式的,parse_methods()先通過(guò) cfs->get_u2_fast() 拿到方法數(shù)量,接著便開(kāi)始進(jìn)行遍歷,調(diào)用 parse_method()依次解析每個(gè)類(lèi)方法。
從Class文件中method_info的定義可知,method 基本上所有信息都存儲(chǔ)在method_info中的attributes[] 數(shù)組中,所以對(duì)于method的解析,基本上也就是在遍歷attributes[] 數(shù)組。
method_info 中的attributes[] 數(shù)組是用來(lái)存放方法的各個(gè)屬性的,其中包括Code屬性、Exception屬性、MethodParameters屬性、Synthetic屬性。parse_method()要做的主要工作,就是遍歷attributes[] 數(shù)組,解析每個(gè)屬性。
下面我們來(lái)便來(lái)各個(gè)擊破,看看上面這些屬性是如何被解析的。
3.1 解析 Code 屬性
(1)獲取maxStacks、maxLocals 和 code length
if(2)獲取字節(jié)碼指令首地址
code_start(3)解析方法中的異常處理表
exception_table_length(4)解析Code屬性中的屬性表,如 LineNumberTables、LocalVariableTables、LocalVariableTypeTables。主要是用于記錄一些調(diào)試信息。
3.2 解析 Exception 屬性
Exception 屬性記錄了方法可能拋出的異常。
checked_exceptions_start3.3 解析 MethodParameters 屬性
MethodParameters 屬性記錄了方法的參數(shù)信息。
method_parameters_seen3.4 解析 Synthetic 屬性
Synthetic 屬性表示成員是在編譯期自動(dòng)為Class生成,如內(nèi)部類(lèi)提供給外部類(lèi)用來(lái)訪問(wèn)內(nèi)部成員的 access()方法。
access_flags如果在解析到該屬性,直接調(diào)用 set_is_synthetic()標(biāo)志下即可。
由上面的解析過(guò)程可知,ClassFileParser 主要就是按照J(rèn)ava虛擬機(jī)規(guī)范對(duì)Class文件結(jié)構(gòu)的定義進(jìn)行流式解析。
四:HotSpot 如何創(chuàng)建 Java 方法
經(jīng)過(guò)第三節(jié)的解析,ClassFileParser 已經(jīng)從Class文件中獲取到了方法的所有信息。接下來(lái)要做的,便是通過(guò)讀取的信息,創(chuàng)建 Java 方法在 JVM 中的數(shù)據(jù)模型。
在HotSpot中,Java方法對(duì)應(yīng)的數(shù)據(jù)結(jié)構(gòu)為 Method,定義在 method.hpp 中
class創(chuàng)建 Method 主要分為下面幾步。
4.1 分配方法對(duì)應(yīng)的 Method
Method4.2 將解析方法時(shí)讀取到信息填充到 Method 中
m->set_constants(_cp); m->set_name_index(name_index); m->set_signature_index(signature_index); ...... // Fill in code attribute information m->set_max_stack(max_stack); m->set_max_locals(max_locals); ...... // Copy byte codes m->set_code((u1*)code_start); ...... // Copy exception table if (exception_table_length > 0) {Copy::conjoint_swap_if_needed<Endian::JAVA>(exception_table_start,m->exception_table_start(),exception_table_length * sizeof(ExceptionTableElement),sizeof(u2)); } ......Method 中將一些只讀數(shù)據(jù)都存放到了它的 _constMethod 中,_constMethod 類(lèi)型為ConstMethod,定義在 constMethod.hpp 中。
舉個(gè)例子,方法的字節(jié)碼指令就存放在 ConstMethod 中,不過(guò)這么說(shuō)不太嚴(yán)謹(jǐn),字節(jié)碼指令并不是直接存放在 ConstMethod 內(nèi)部,而是緊跟著 ConstMethod 存放在內(nèi)存中。
我們?cè)倏纯瓷厦娴奶畛溥壿?#xff0c;調(diào)用了 m->set_code((u1*)code_start) 來(lái)存放字節(jié)碼指令首地址,Method 其實(shí)是直接調(diào)用了 ConstMethod 的 set_code():
voidConstMethod的set_code()也很簡(jiǎn)單:
void首先調(diào)用code_base()獲取存放字節(jié)碼的地址,接著便調(diào)用memcpy(),將字節(jié)碼指令從 code 處拷貝到code_base()處。
code_base()代碼如下:
address因?yàn)?this 本身是指針,所以 this + 1 獲取的地址為:
constMethod 首地址 + sizeOf(ConstMethod)所以字節(jié)碼指令存放在ConstMethod之后:
存放好字節(jié)碼指令后,以后當(dāng)調(diào)用該方法時(shí),就可以從 ConstMethod 中獲取到字節(jié)碼指令首地址,從而進(jìn)行取指執(zhí)行了。
五:HotSpot 如何鏈接 Java 方法
上面只是在加載Class文件時(shí)對(duì)Java方法進(jìn)行了解析和創(chuàng)建,而Java 方法的鏈接是發(fā)生在所屬I(mǎi)nstanceKlass 的初始化時(shí)期。
一般來(lái)說(shuō),Class文件在被加載成 InstanceKlass 后不會(huì)立即初始化,而是等到實(shí)例化 Obejct、反射獲取字段、方法信息,或者調(diào)用static方法等時(shí)機(jī)才會(huì)初始化。
InstanceKlass在初始化時(shí)會(huì)調(diào)用 link_class()對(duì)類(lèi)進(jìn)行鏈接,在類(lèi)的鏈接過(guò)程中,便會(huì)調(diào)用 InstanceKlass的 link_methods()方法,對(duì)類(lèi)的所有方法進(jìn)行鏈接。
對(duì)單個(gè) Method 進(jìn)行鏈接的方法為:Method::link_method(const methodHandle& h_method, TRAPS),方法鏈接主要就是做的事就是設(shè)置 Method 的 interpreter_entry:
address上面首先通過(guò) entry_for_method(h_method)獲取方法的入口例程,關(guān)于這個(gè)例程是干什么的,可以看看之前寫(xiě)的《JVM方法執(zhí)行的來(lái)龍去脈》,簡(jiǎn)單來(lái)說(shuō),HotSpot對(duì)于Java方法的執(zhí)行不是簡(jiǎn)單的從方法字節(jié)碼首地址處進(jìn)行取指執(zhí)行即可,在進(jìn)行字節(jié)碼指令執(zhí)行之前,需要為Java方法創(chuàng)建棧幀、局部變量表等事情,而這些事情是通用的,所以HotSpot將這些事情統(tǒng)一到一起,對(duì)Java方法的執(zhí)行做了一層封裝,而例程便是這個(gè)封裝的入口。
HotSpot 提前為各種類(lèi)型的方法創(chuàng)建好了一系列例程,所以 entry_for_method(h_method)便是根據(jù)方法類(lèi)型,從例程表中查詢(xún)到對(duì)應(yīng)類(lèi)型的例程。
查詢(xún)到例程后,便調(diào)用set_interpreter_entry(entry),將例程的入口地址保存到Method中:
void保存例程的入口地址后,以后調(diào)用Java方法時(shí),便可以從Method中獲取例程的入口地址,跳到此處執(zhí)行。
六:總結(jié)
通過(guò)上面的分析,我們了解了Java方法在Class文件中的結(jié)構(gòu),以及方法的解析、創(chuàng)建及鏈接。
解析過(guò)程主要是流式讀取Class文件,獲取方法在Class文件中的信息。
創(chuàng)建過(guò)程主要是創(chuàng)建Java方法對(duì)應(yīng)的Method,并將解析過(guò)程讀取的信息填充到Method中。
鏈接過(guò)程主要是根據(jù)方法類(lèi)型,獲取并保存方法對(duì)應(yīng)的入口例程的地址。
我的文章只是個(gè)引子,畢竟短短篇幅無(wú)法囊括JVM浩瀚如煙的源碼。如果想對(duì) HotSpot 如何處理 Java 方法的細(xì)節(jié)深入了解的話(huà),想必最好的方式還是自己去閱讀和調(diào)試 OpenJDK,
罷了,寫(xiě)完,關(guān)燈,睡覺(jué)!
聽(tīng)說(shuō)喜歡點(diǎn)關(guān)注的同學(xué)都長(zhǎng)得帥
總結(jié)
以上是生活随笔為你收集整理的java解析dxf文件_浅析JVM方法解析、创建和链接的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 三面玻璃视野绝了!鑫谷斜杠青年mini机
- 下一篇: 由于在客户端检测到一个协议错误_HTTP