- 深入解析Java虛擬機(jī)HotSpot
- 楊易
- 1400字
- 2021-01-07 11:18:29
3.3 類
Klass是一個(gè)抽象基類,它定義了一些接口(純虛函數(shù)),由InstanceKlass繼承并實(shí)現(xiàn)這些接口,兩者結(jié)合可以描述一個(gè)Java類的方法有哪些、字段有哪些、父類是否存在等。Klass提供了相當(dāng)多的關(guān)于類的信息,同樣可以使用HotSpot Debugger可視化,如圖3-5所示。
InstanceKlass在虛擬機(jī)層描述大部分的Java類,但有少部分Java類有特殊語意:普通類的對(duì)象在垃圾回收過程中只需要遍歷所有實(shí)例字段;java.lang.Class的對(duì)象需要遍歷實(shí)例字段和靜態(tài)字段;java.lang.ref.*的對(duì)象需要處理被引用對(duì)象;java.lang.ClassLoader需要處理類加載數(shù)據(jù)。這些類的特殊行為不能用InstanceKlass統(tǒng)一表示,因此InstanceKlass之下派生出InstanceMirrorKlass描述java.lang.Class類,InstanceRefKlass描述java.lang.ref.*類,InstanceClassLoaderKlass描述java.lang.ClassLoader類。
3.3.1 字段遍歷
在垃圾回收過程中常見的任務(wù)是遍歷一個(gè)對(duì)象的所有字段。以G1為例,在Full GC過程中會(huì)標(biāo)記所有成員對(duì)象,如代碼清單3-5所示:
代碼清單3-5 字段遍歷
inline void G1FullGCMarker::follow_object(oop obj) { if (obj->is_objArray()) { // 如果對(duì)象是數(shù)組,則標(biāo)記每個(gè)數(shù)組成員 follow_array((objArrayOop)obj); } else { // 否則標(biāo)記對(duì)象的每個(gè)非靜態(tài)數(shù)據(jù)成員 obj->oop_iterate(mark_closure()); } } // 該方法由上面的obj->oop_iterate調(diào)用 ALWAYSINLINE void InstanceKlass::oop_oop_iterate_oop_maps(...) { OopMapBlock* map = start_of_nonstatic_oop_maps(); OopMapBlock* const end_map = map + nonstatic_oop_map_count(); for (; map < end_map; ++map) { // 遍歷每個(gè)OopMapBlock oop_oop_iterate_oop_map<T>(map, obj, closure); } }
調(diào)用obj->oop_iterate后經(jīng)過一個(gè)較長(zhǎng)的調(diào)用鏈,會(huì)執(zhí)行oop_oop_iterate_oop_maps,根據(jù)代碼不難看出它的行為是獲取開始OopMapBlock和結(jié)束OopMapBlock,然后遍歷這些OopMapBlock。OopMapBlock存儲(chǔ)了該對(duì)象的字段偏移和個(gè)數(shù),分別用offset和count表示。offset表示第一個(gè)字段相對(duì)于對(duì)象頭的偏移,count表示對(duì)象有多少個(gè)字段。另外,如果有父類,則再用一個(gè)OopMapBlock表示父類,因此通過遍歷對(duì)象的所有OopMapBlock就能訪問對(duì)象的全部字段。
3.3.2 虛表
如果使用C++編程,會(huì)用一個(gè)Node表示基類,由AddNode繼承Node,它們都有一個(gè)print方法。現(xiàn)在有一個(gè)變量Node *n=new AddNode,靜態(tài)類型為Node,動(dòng)態(tài)類型為AddNode,調(diào)用n->print()函數(shù)會(huì)根據(jù)n的動(dòng)態(tài)類型進(jìn)行函數(shù)派發(fā),由于n的動(dòng)態(tài)類型為AddNode所以調(diào)用AddNode::print。在這個(gè)過程中,需要為每個(gè)對(duì)象插入一個(gè)虛表。虛表是一個(gè)由函數(shù)指針構(gòu)成的數(shù)組,可以添加編譯參數(shù)輸出它[1]。以上面的變量為例,Node虛表的第一個(gè)元素是指向Node::print的函數(shù)指針,AddNode虛表的第一個(gè)元素是指向AddNode::print的指針,n在運(yùn)行時(shí)可以通過查找虛表來定位正確的方法(AddNode::print)。
如果使用Java編程,情況就不一樣了。根據(jù)前面的描述,每個(gè)Java對(duì)象即oop都有對(duì)象頭,對(duì)象頭里面有一個(gè)_klass指向?qū)ο蟮恼_的InstanceKlass類型,而InstanceKlass包含了類的所有方法以及父類信息,當(dāng)執(zhí)行n.print()時(shí),JVM可以(但是并沒有)從對(duì)象n的對(duì)象頭里取出_klass,找到描述AddNode類的InstanceKlass,再在其中尋找print方法。這一過程并不需要虛表參與。正如上面討論的,虛表是Java動(dòng)態(tài)派發(fā)的優(yōu)化而不是必要組件,就像native入口之于Method,Java的虛表也是位于InstanceKlass之外,如圖3-6所示。
第2章提到類會(huì)經(jīng)歷加載、鏈接、初始化三個(gè)階段,這里我們只討論了鏈接階段的一些步驟,實(shí)際上它還會(huì)執(zhí)行很多額外的步驟,如虛表的初始化也是在鏈接階段進(jìn)行的。HotSpot會(huì)在類加載階段計(jì)算出虛表大小,然后在鏈接階段使用klassVtable::initialize_vtable()初始化虛表,如代碼清單3-6所示:
代碼清單3-6 虛表初始化
void klassVtable::initialize_vtable(bool checkconstraints, TRAPS) { ... // 處理當(dāng)前類的所有方法 for(int i = 0; i < len; i++) { methodHandle mh(THREAD, methods->at(i)); // 該方法是否為虛方法 bool needs_new_entry = update_inherited_vtable(...); // 如果是,則需要更新當(dāng)前類的虛表索引 if (needs_new_entry) { put_method_at(mh(), initialized); mh()->set_vtable_index(initialized); initialized++; } } ...// 和前面類似,處理default方法 }
update_inherited_vtable會(huì)經(jīng)過一系列檢查來確定一個(gè)方法是否為虛方法以及是否需要加入類的虛表。上述例子的Node與AddNode經(jīng)過虛表初始化后的vtable如圖3-7所示。
也可以開啟VM參數(shù)-Xlog:vtables=trace查看所有類的虛表的創(chuàng)建過程。在調(diào)用虛方法時(shí)虛擬機(jī)會(huì)在運(yùn)行時(shí)常量池中查找n的靜態(tài)類型Node的print方法,獲取它在Node虛表中的index,接著用index定位動(dòng)態(tài)類型AddNode虛表中的虛方法進(jìn)行調(diào)用。第一步的運(yùn)行時(shí)常量池在HotSpot VM中的表示是oops/ConstantPoolCache,它也是對(duì)象和類模型的一部分。
[1] gcc使用-fdump-class-hierarchy輸出虛表,clang使用-Xclang -fdump-vtable-layouts輸出虛表,msvc使用/d1reportAllClassLayout輸出虛表。
- Practical Data Analysis Cookbook
- SQL Server 從入門到項(xiàng)目實(shí)踐(超值版)
- Go Web編程
- The Modern C++ Challenge
- TypeScript Blueprints
- Learning RxJava
- PaaS程序設(shè)計(jì)
- 教孩子學(xué)編程:C++入門圖解
- 微服務(wù)從小白到專家:Spring Cloud和Kubernetes實(shí)戰(zhàn)
- 區(qū)塊鏈技術(shù)與應(yīng)用
- Windows Embedded CE 6.0程序設(shè)計(jì)實(shí)戰(zhàn)
- Creating Data Stories with Tableau Public
- 快速入門與進(jìn)階:Creo 4·0全實(shí)例精講
- RESTful Web Clients:基于超媒體的可復(fù)用客戶端
- SCRATCH編程課:我的游戲我做主