新聞中心
JVM是Java Virtual Machine(Java虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來的計(jì)算機(jī),是通過在實(shí)際的計(jì)算機(jī)上仿真模擬各種計(jì)算機(jī)功能來實(shí)現(xiàn)的。 引入Java語言虛擬機(jī)后,Java語言在不同平臺(tái)上運(yùn)行時(shí)不需要重新編譯。Java語言使用Java虛擬機(jī)屏蔽了與具體平臺(tái)相關(guān)的信息,使得Java語言編譯程序只需生成在Java虛擬機(jī)上運(yùn)行的目標(biāo)代碼(字節(jié)碼),就可以在多種平臺(tái)上不加修改地運(yùn)行。

創(chuàng)新互聯(lián)專注于大連企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),電子商務(wù)商城網(wǎng)站建設(shè)。大連網(wǎng)站建設(shè)公司,為大連等地區(qū)提供建站服務(wù)。全流程按需設(shè)計(jì)網(wǎng)站,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)
本篇就給大家分析類加載場(chǎng)景,希望能對(duì)你有所幫助。
A類調(diào)用B類的靜態(tài)方法,除了加載B類,但是B類的一個(gè)未被調(diào)用的方法間接使用到的C類卻也被加載了,這個(gè)有意思的場(chǎng)景來自一個(gè)提問:方法中使用的類型為何在未調(diào)用時(shí)嘗試加載?。
場(chǎng)景如下:
- public class Main {
- static {
- System.out.println("Main static block");
- }
- public static void main(String[] args) {
- Helper.staticMethod();
- }
- }
- public class Helper {
- static {
- System.out.println("Helper static block");
- }
- public static void staticMethod() {
- System.out.println("Helper#staticMethod");
- }
- public void test(XXXManager ab, XXXSubInterface xxxSubInterface) {
- ab.setXXX(xxxSubInterface);
- }
- }
- public interface XXX {}
- public interface XXXSubInterface extends XXX {}
- public interface XXXManager {
- void setXXX(XXX xxx);
- }
添加JVM -varbose參數(shù)進(jìn)行執(zhí)行,輸出是:
- [Loaded Main from file:/Users/mazhibin/project/java/loadclasstest/target/classes/]
- Main static block
- [Loaded Helper from file:/Users/mazhibin/project/java/loadclasstest/target/classes/]
- [Loaded XXX from file:/Users/mazhibin/project/java/loadclasstest/target/classes/]
- Helper static block
- Helper#staticMethod
main方法執(zhí)行Helper.staticMethod(),而staticMethod方法里面只有打印語句,所以理論上應(yīng)該只要加載Helper就夠了,為了什么會(huì)加載到XXX類,好,即使接受可以加載類的情況,為什么是XXX,而不是直接使用到的XXXManager或者XXXSubInterface。你提的問題大概是這個(gè)場(chǎng)景。
在說探索過程之前先說下最終結(jié)論:在驗(yàn)證Helper類時(shí),校驗(yàn)到setXXX方法,會(huì)驗(yàn)證XXXSubInterface類型是否可以賦值到XXX類型,這個(gè)時(shí)候就會(huì)去加載XXX類,然后因?yàn)閄XX是一個(gè)接口,代碼中認(rèn)為接口和Object類是一樣的,什么類型都可以賦值給接口類型,所以就直接校驗(yàn)成功,就沒有去加載XXXSubInterface類了。
然后在介紹一下類加載的過程。首先要清楚一點(diǎn),“類加載”和“加載”是兩個(gè)概念,“加載”是“類加載”(Class Loading)的一個(gè)步驟。類加載包含加載、鏈接、初始化這三個(gè)步驟,其中鏈接又分為驗(yàn)證、準(zhǔn)備、解析這三個(gè)子步驟。加載是根據(jù)特定名稱查找類或接口類型的二進(jìn)制表示(Binary Representation),并由此二進(jìn)制表示創(chuàng)建類或接口的過程。鏈接是為了讓類或接口可以被 Java 虛擬機(jī)執(zhí)行,而將類或接口并入虛擬機(jī)運(yùn)行時(shí)狀態(tài)的過程。類或接口的初始化是指執(zhí)行類或接口的初始化方法 。
類加載復(fù)雜就復(fù)雜在這些步驟執(zhí)行的時(shí)機(jī),并且其中的子步驟還不一定按順序執(zhí)行,加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這5個(gè)階段的順序是固定的,需要按這個(gè)順序開始(允許交叉),而解析則不一定,有可能在初始化之后才進(jìn)行。
那什么時(shí)候會(huì)開始加載步驟?Java虛擬機(jī)規(guī)范沒有強(qiáng)制要求,但是對(duì)于初始化階段,則明確規(guī)定了5種情況需要對(duì)類進(jìn)行初始化,分別是:
- 在執(zhí)行下列需要引用類或接口的Java虛擬機(jī)指令時(shí):new,getstatic,putstatic或invokestatic。這些指令通過字段或方法引用來直接或間接地引用其它類。執(zhí)行上面所述的new指令,在類或接口沒有被初始化過時(shí)就初始化它。執(zhí)行上面的getstatic,putstatic或invokestatic指令時(shí),那些解析好的字段或方法中的類或接口如果還沒有被初始化那就初始化它。
- 在初次調(diào)用java.lang.invoke.MethodHandle實(shí)例時(shí),它的執(zhí)行結(jié)果為通過Java虛擬機(jī)解析出類型是2(REF_getStatic)、4(REF_putStatic)或者6(REF_invokeStatic)的方法句柄(§5.4.3.5)。
- 在調(diào)用JDK核心類庫(kù)中的反射方法時(shí),例如,Class類或java.lang.reflect包。
- 在對(duì)于類的某個(gè)子類的初始化時(shí)。
- 在它被選定為Java虛擬機(jī)啟動(dòng)時(shí)的初始類(§5.2)時(shí)。
結(jié)合上面說的,加載、驗(yàn)證、準(zhǔn)備、初始化和卸載這5個(gè)階段的順序是固定的,需要按這個(gè)順序開始(允許交叉),我們確定了初始化的時(shí)機(jī),那么在初始化時(shí)或者之前,就要開始加載了。同時(shí)還有一點(diǎn),也就是這個(gè)問題涉及到的場(chǎng)景,一個(gè)類在驗(yàn)證這個(gè)步驟時(shí),會(huì)驗(yàn)證A類的字節(jié)碼,其中可能會(huì)涉及到所以來的其他類,根據(jù)驗(yàn)證的具體需求,可能需要加載其他類。而這個(gè)問題具體的校驗(yàn)過程就是一個(gè)方法調(diào)用,涉及到類型轉(zhuǎn)換賦值(傳入子接口類型,需要轉(zhuǎn)為父接口類型),這種情況下需要加載類型來判斷是否可以進(jìn)行賦值,按理是需要加載賦值左右兩邊的類型的,但是因?yàn)樽筮咁愋褪墙涌冢徽J(rèn)為都可以賦值,所以沒有加載右邊類型。
總結(jié)來說可能的加載時(shí)機(jī)為以下幾點(diǎn)(不一定全面,是我目前已知的):
- JVM啟動(dòng)時(shí)會(huì)預(yù)加載一些核心類,比如Object、String等
- 這個(gè)類被第一次真正使用到時(shí),比如主類因?yàn)橐{(diào)用其main方法,自然需要被加載
- 在A類校驗(yàn)階段,可能需要加載其代碼中使用到的B類
- 在A類進(jìn)行某個(gè)符號(hào)引用的解析時(shí),需要加載對(duì)應(yīng)的B類。比如正在執(zhí)行A類的一個(gè)方法,執(zhí)行到B.func(),那就需要解析B和func符號(hào)引用為直接引用,那自然需要加載B類到方法區(qū)中
接下來說下是如何得到上述結(jié)論的,首先類加載的流程是Java虛擬機(jī)規(guī)范中有寫的,可以看看。而具體為什么只加載了XXX類,則要調(diào)試JVM源碼才能知道了。最近因?yàn)橛锌碕VM源碼,所以編譯了并可以進(jìn)行GDB調(diào)試,然后添加條件斷點(diǎn):break SystemDictionary::load_instance_class if strncmp(class_name._body, "XXX", 3) == 0,表示在加載XXX類的時(shí)候停下來,接著分析調(diào)用堆棧:
- / 加載Main類
- [Loaded Main from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/]
- // 執(zhí)行Main的初始化方法
- Main static block
- // 因?yàn)橐獔?zhí)行Helper.staticMethod()語句,觸發(fā)加載Helper流程
- [Loaded Helper from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/]
- // 接著斷點(diǎn)停在了加載XXX接口的函數(shù)調(diào)用上
- Breakpoint 1, SystemDictionary::load_instance_class (class_name=0x7ffff01a5338, class_loader=..., __the_thread__=
- 0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345
- 1345 instanceKlassHandle nh = instanceKlassHandle(); // null Handle
- // 查看函數(shù)調(diào)用棧,分析為什么會(huì)需要加載XXX類(要從下往上看)
- (gdb) bt
- #0 SystemDictionary::load_instance_class (class_name=0x7ffff01a5338, class_loader=...,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345
- #1 0x00007ffff7578062 in SystemDictionary::resolve_instance_class_or_null (name=0x7ffff01a5338,
- class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:755
- #2 0x00007ffff7576a17 in SystemDictionary::resolve_or_null (class_name=0x7ffff01a5338, class_loader=...,
- protection_domain=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:203
- #3 0x00007ffff75765ad in SystemDictionary::resolve_or_fail (class_name=0x7ffff01a5338, class_loader=...,
- protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:145
- // 上面就開始了加載流程了
- // 下文分析了這里是在校驗(yàn)XXXSubInterface類型是否可以賦值到XXX
- // 下文分析了為什么需要加載XXX接口,而不需要加載XXXSubInterface接口
- #4 0x00007ffff75df854 in VerificationType::is_reference_assignable_from (this=0x7ffff7fe5770, from=..., context=
- 0x7ffff7fe60e0, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verificationType.cpp:62
- #5 0x00007ffff753bc37 in VerificationType::is_assignable_from (this=0x7ffff7fe5770, from=...,
- context=0x7ffff7fe60e0, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verificationType.hpp:289
- #6 0x00007ffff75eba80 in StackMapFrame::pop_stack (this=0x7ffff7fe5e20, type=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/stackMapFrame.hpp:181
- #7 0x00007ffff75ea155 in ClassVerifier::verify_invoke_instructions (this=0x7ffff7fe60e0, bcs=0x7ffff7fe5dc0,
- code_length=18, current_frame=0x7ffff7fe5e20, this_uninit=0x7ffff7fe5f1f, return_type=..., cp=...,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verifier.cpp:2064
- // 下文分析了這里是因?yàn)轵?yàn)證Helper.test(LXXXManager;)V這個(gè)方法導(dǎo)致的加載XXX接口
- #8 0x00007ffff75e64ea in ClassVerifier::verify_method (this=0x7ffff7fe60e0, m=...,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verifier.cpp:1237
- #9 0x00007ffff75e0e75 in ClassVerifier::verify_class (this=0x7ffff7fe60e0, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verifier.cpp:312
- #10 0x00007ffff75e04b1 in Verifier::verify (klass=..., mode=Verifier::ThrowException, should_verify_class=true,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verifier.cpp:127
- #11 0x00007ffff71f5d8c in instanceKlass::verify_code (this_oop=..., throw_verifyerror=true,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp:214
- // 上面的方法是驗(yàn)證的過程,也就是校驗(yàn)字節(jié)碼是否正確,是否合法
- #12 0x00007ffff71f6425 in instanceKlass::link_class_impl (this_oop=..., throw_verifyerror=true,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp:321
- #13 0x00007ffff71f5e73 in instanceKlass::link_class (this=0xfb01ab80, __the_thread__=0x7ffff0028000)
- // 在類或接口被初始化之前,它必須被鏈接過,也就是經(jīng)過驗(yàn)證、準(zhǔn)備階段,且有可能已經(jīng)被解析完成了。所以上面是鏈接的流程
- at /root/jvm/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp:230
- #14 0x00007ffff71f691f in instanceKlass::initialize_impl (this_oop=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp:397
- #15 0x00007ffff71f5cca in instanceKlass::initialize (this=0xfb01ab80, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/oops/instanceKlass.cpp:199
- #16 0x00007ffff7383903 in LinkResolver::resolve_static_call (result=..., resolved_klass=...,
- method_name=0x7ffff01a4908, method_signature=0x7ffff0051f28, current_klass=..., check_access=true,
- initialize_class=true, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:629
- // Main類在運(yùn)行字節(jié)碼時(shí),執(zhí)行到invokestatic指令,對(duì)應(yīng)的語句是Helper.staticMethod()
- // JVM規(guī)范中說明,在執(zhí)行new,getstatic,putstatic或invokestatic這些指令時(shí),需要確保目標(biāo)類已經(jīng)進(jìn)行初始化流程
- // 而初始化流程需要確保目標(biāo)類已經(jīng)被加載、驗(yàn)證、準(zhǔn)備,所以上面會(huì)走到Helper的加載、驗(yàn)證、準(zhǔn)備的流程
- // 這個(gè)堆棧跟蹤到的是驗(yàn)證的流程
- #17 0x00007ffff738599f in LinkResolver::resolve_invokestatic (result=..., pool=..., index=65537,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1077
- #18 0x00007ffff738575c in LinkResolver::resolve_invoke (result=..., recv=..., pool=..., index=65537,
- byte=Bytecodes::_invokestatic, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1050
- #19 0x00007ffff7239c58 in InterpreterRuntime::resolve_invoke (thread=0x7ffff0028000,
- bytecode=Bytecodes::_invokestatic)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp:686
- // 我們到第16號(hào)棧幀中,可以看出的確是正要執(zhí)行Helper.staticMethod()方法
- (gdb) f 16
- #16 0x00007ffff7383903 in LinkResolver::resolve_static_call (result=..., resolved_klass=...,
- method_name=0x7ffff01a4908, method_signature=0x7ffff0051f28, current_klass=..., check_access=true,
- initialize_class=true, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:629
- 629 resolved_klass->initialize(CHECK);
- (gdb) p Klass::cast(current_klass.obj())->external_name()
- $1 = 0x7fffcc002548 "Main"
- (gdb) p *method_name._body@method_name._length
- $5 = "staticMethod"
- // 我們到第8號(hào)棧幀中,可以看出是因?yàn)轵?yàn)證Helper.test(LXXXManager;)V這個(gè)方法導(dǎo)致的加載XXX接口
- (gdb) f 8
- #8 0x00007ffff75e64ea in ClassVerifier::verify_method (this=0x7ffff7fe60e0, m=...,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verifier.cpp:1237
- 1237 &this_uninit, return_type, cp, CHECK_VERIFY(this));
- (gdb) p m->name_and_sig_as_C_string()
- $6 = 0x7fffcc002568 "Helper.test(LXXXManager;)V"
- // 我們到第4號(hào)棧幀中,可以看出是在校驗(yàn)XXXSubInterface類型是否可以賦值到XXX
- (gdb) f 4
- #4 0x00007ffff75df854 in VerificationType::is_reference_assignable_from (this=0x7ffff7fe5770, from=...,
- context=0x7ffff7fe60e0, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/verificationType.cpp:62
- 62 Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- (gdb) p *from.name()._body@from.name()._length
- $10 = "XXXSubInterface"
- (gdb) p *name()._body@name()._length
- $11 = "XXX"
上面分析出了加載是因?yàn)轵?yàn)證的流程,具體觸發(fā)加載的驗(yàn)證代碼如下,是驗(yàn)證賦值操作是否可以成功的:
- // hotspot/src/share/vm/classfile/verificationType.cpp
- bool VerificationType::is_reference_assignable_from(
- const VerificationType& from, ClassVerifier* context, TRAPS) const {
- instanceKlassHandle klass = context->current_class();
- if (from.is_null()) {
- // null is assignable to any reference
- return true;
- } else if (is_null()) {
- return false;
- } else if (name() == from.name()) {
- return true;
- } else if (is_object()) {
- // 如果賦值語句左邊類型是對(duì)象,判斷是否是Object,如果是那都可以賦值成功,返回true
- // We need check the class hierarchy to check assignability
- if (name() == vmSymbols::java_lang_Object()) {
- // any object or array is assignable to java.lang.Object
- return true;
- }
- // 否則需要把左邊類型加載進(jìn)來 <=========================== 加載行為發(fā)生在這里
- klassOop obj = SystemDictionary::resolve_or_fail(
- name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- KlassHandle this_class(THREAD, obj);
- // 如果左邊類型是接口
- if (this_class->is_interface()) {
- // 這里注釋說明了,認(rèn)為接口和Object一樣,都可以賦值成功所以返回true
- // We treat interfaces as java.lang.Object, including
- // java.lang.Cloneable and java.io.Serializable
- return true;
- } else if (from.is_object()) {
- // 否則要把賦值賦予右邊的類型也加載進(jìn)來
- klassOop from_class = SystemDictionary::resolve_or_fail(
- from.name(), Handle(THREAD, klass->class_loader()),
- Handle(THREAD, klass->protection_domain()), true, CHECK_false);
- return instanceKlass::cast(from_class)->is_subclass_of(this_class());
- }
- } else if (is_array() && from.is_array()) {
- VerificationType comp_this = get_component(context, CHECK_false);
- VerificationType comp_from = from.get_component(context, CHECK_false);
- if (!comp_this.is_bogus() && !comp_from.is_bogus()) {
- return comp_this.is_assignable_from(comp_from, context, CHECK_false);
- }
- }
- return false;
- }
這樣就分析完了,嘗試把XXX和XXXSubInterface改成class,可以發(fā)現(xiàn)兩個(gè)都會(huì)被加載,符合上面這個(gè)代碼的邏輯。
接著順便分析一下Helper類加載的堆棧:
- // 加載Main類
- [Loaded Main from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/]
- // 執(zhí)行Main初始化方法
- Main static block
- // 斷點(diǎn)停在加載Helper類的邏輯上
- Breakpoint 2, SystemDictionary::load_instance_class (class_name=0x7ffff01a60d8, class_loader=..., __the_thread__=
- 0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345
- 1345 instanceKlassHandle nh = instanceKlassHandle(); // null Handle
- (gdb) bt
- #0 SystemDictionary::load_instance_class (class_name=0x7ffff01a60d8, class_loader=...,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345
- #1 0x00007ffff7578062 in SystemDictionary::resolve_instance_class_or_null (name=0x7ffff01a60d8,
- class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:755
- #2 0x00007ffff7576a17 in SystemDictionary::resolve_or_null (class_name=0x7ffff01a60d8, class_loader=...,
- protection_domain=..., __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:203
- #3 0x00007ffff75765ad in SystemDictionary::resolve_or_fail (class_name=0x7ffff01a60d8, class_loader=...,
- protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:145
- #4 0x00007ffff70c1915 in constantPoolOopDesc::klass_at_impl (this_oop=..., which=18,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:102
- #5 0x00007ffff6fa1f69 in constantPoolOopDesc::klass_at (this=0xfb019e90, which=18,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.hpp:366
- #6 0x00007ffff70c2c84 in constantPoolOopDesc::klass_ref_at (this=0xfb019e90, which=65537,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:382
- #7 0x00007ffff73817c0 in LinkResolver::resolve_klass (result=..., pool=..., index=65537,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:161
- #8 0x00007ffff7385871 in LinkResolver::resolve_pool (resolved_klass=..., method_name=@0x7ffff7fe6638: 0x0,
- method_signature=@0x7ffff7fe6630: 0x0, current_klass=..., pool=..., index=65537,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1062
- // Main類在運(yùn)行字節(jié)碼時(shí),執(zhí)行到invokestatic指令,對(duì)應(yīng)的語句是Helper.staticMethod()
- // JVM規(guī)范中說明,指令anewarray、checkcast、getfield、getstatic、instanceof、nvokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield和putstatic將符號(hào)引用指向運(yùn)行時(shí)常量池,執(zhí)行上述任何一條指令都需要對(duì)它的符號(hào)引用的進(jìn)行解析。
- // 所以這里需要將Main中的運(yùn)行時(shí)常量池中的Helper和staticMethod進(jìn)行符號(hào)解析
- // 符號(hào)解析是把符號(hào)引用替換為真實(shí)引用,自然需要加載Helper類,才能進(jìn)行替換,所以上面就觸發(fā)了Helper的加載流程
- #9 0x00007ffff738595b in LinkResolver::resolve_invokestatic (result=..., pool=..., index=65537,
- __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1076
- #10 0x00007ffff738575c in LinkResolver::resolve_invoke (result=..., recv=..., pool=..., index=65537,
- byte=Bytecodes::_invokestatic, __the_thread__=0x7ffff0028000)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1050
- #11 0x00007ffff7239c58 in InterpreterRuntime::resolve_invoke (thread=0x7ffff0028000,
- ---Type
to continue, or q to quit--- - bytecode=Bytecodes::_invokestatic)
- at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/interpreterRuntime.cpp:686
- // hotspot/src/share/vm/interpreter/linkResolver.cpp
- void LinkResolver::resolve_invokestatic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {
- KlassHandle resolved_klass;
- Symbol* method_name = NULL;
- Symbol* method_signature = NULL;
- KlassHandle current_klass;
- // 解析常量池中的符號(hào)引用,會(huì)觸發(fā)加載被調(diào)用類的流程 <==================
- resolve_pool(resolved_klass, method_name, method_signature, current_klass, pool, index, CHECK);
- // 解析從方法簽名解析出方法oop,會(huì)觸發(fā)類的初始化流程 <==================
- resolve_static_call(result, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK);
- }
總結(jié)
總結(jié)來說可能的加載時(shí)機(jī)為以下幾點(diǎn)(不一定全面,是我目前已知的):
- JVM啟動(dòng)時(shí)會(huì)預(yù)加載一些核心類,比如Object、String等
- 這個(gè)類被第一次真正使用到時(shí),比如主類因?yàn)橐{(diào)用其main方法,自然需要被加載
- 在A類校驗(yàn)階段,可能需要加載其代碼中使用到的B類
- 在A類進(jìn)行某個(gè)符號(hào)引用的解析時(shí),需要加載對(duì)應(yīng)的B類。比如正在執(zhí)行A類的一個(gè)方法,執(zhí)行到B.func(),那就需要解析B和func符號(hào)引用為直接引用,那自然需要加載B類到方法區(qū)中
對(duì)應(yīng)A類調(diào)用B類的情況,JVM規(guī)范中說明,指令anewarray、checkcast、getfield、getstatic、instanceof、nvokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield和putstatic將符號(hào)引用指向運(yùn)行時(shí)常量池,執(zhí)行上述任何一條指令都需要對(duì)它的符號(hào)引用的進(jìn)行解析,所以需要解析A類中對(duì)B類的符號(hào)引用,而解析是把符號(hào)引用替換為真實(shí)引用,所以需要把B類加載到方法區(qū)中,這就觸發(fā)了加載流程。
而B類的鏈接流程,則是因?yàn)镴VM規(guī)范中說明,在執(zhí)行new,getstatic,putstatic或invokestatic這些指令時(shí),需要確保目標(biāo)類已經(jīng)進(jìn)行初始化流程,而初始化流程需要確保目標(biāo)類已經(jīng)被加載、驗(yàn)證、準(zhǔn)備,而加載之前執(zhí)行過了,所以需要進(jìn)入驗(yàn)證和準(zhǔn)備的流程。而鏈接中的解析過程不會(huì)執(zhí)行,B類的解析會(huì)在執(zhí)行B類中相關(guān)代碼時(shí)再進(jìn)行。
上面說的兩個(gè)過程都是在執(zhí)行字節(jié)碼時(shí)觸發(fā)的,比如invokestaic。而B類在驗(yàn)證的過程中,可能又會(huì)需要加載其代碼中使用到的C類。
當(dāng)前標(biāo)題:JVM(JavaVirtualMachine)源碼分析-類加載場(chǎng)景實(shí)例分析
網(wǎng)站URL:http://www.dlmjj.cn/article/dhijipi.html


咨詢
建站咨詢
