iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >JVM类加载场景的实例分析
  • 900
分享到

JVM类加载场景的实例分析

2023-06-16 01:06:46 900人浏览 薄情痞子
摘要

JVM类加载场景的实例分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。JVM是Java Virtual  Machine(Java虚拟机)的缩写,J

JVM类加载场景的实例分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

JVM是Java Virtual  Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。  引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

给大家分析类加载场景,希望能对你有所帮助。

A类调用B类的静态方法,除了加载B类,但是B类的一个未被调用的方法间接使用到的C类却也被加载了,这个有意思的场景来自一个提问:方法中使用的类型为何在未调用时尝试加载?。

场景如下:

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参数进行执行,输出是:

[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方法执行Helper.staticMethod(),而staticMethod方法里面只有打印语句,所以理论上应该只要加载Helper就够了,为了什么会加载到XXX类,好,即使接受可以加载类的情况,为什么是XXX,而不是直接使用到的XXXManager或者XXXSubInterface。你提的问题大概是这个场景。

在说探索过程之前先说下最终结论:在验证Helper类时,校验到setXXX方法,会验证XXXSubInterface类型是否可以赋值到XXX类型,这个时候就会去加载XXX类,然后因为XXX是一个接口,代码中认为接口和Object类是一样的,什么类型都可以赋值给接口类型,所以就直接校验成功,就没有去加载XXXSubInterface类了。

然后在介绍一下类加载的过程。首先要清楚一点,“类加载”和“加载”是两个概念,“加载”是“类加载”(Class  Loading)的一个步骤。类加载包含加载、链接、初始化这三个步骤,其中链接又分为验证、准备、解析这三个子步骤。加载是根据特定名称查找类或接口类型的二进制表示(Binary  Representation),并由此二进制表示创建类或接口的过程。链接是为了让类或接口可以被 Java  虚拟机执行,而将类或接口并入虚拟机运行时状态的过程。类或接口的初始化是指执行类或接口的初始化方法

JVM类加载场景的实例分析

类加载复杂就复杂在这些步骤执行的时机,并且其中的子步骤还不一定按顺序执行,加载、验证、准备、初始化和卸载这5个阶段的顺序是固定的,需要按这个顺序开始(允许交叉),而解析则不一定,有可能在初始化之后才进行。

那什么时候会开始加载步骤?Java虚拟机规范没有强制要求,但是对于初始化阶段,则明确规定了5种情况需要对类进行初始化,分别是:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 在执行下列需要引用类或接口的Java虚拟机指令时:new,getstatic,putstatic或invokestatic。这些指令通过字段或方法引用来直接或间接地引用其它类。执行上面所述的new指令,在类或接口没有被初始化过时就初始化它。执行上面的getstatic,putstatic或invokestatic指令时,那些解析好的字段或方法中的类或接口如果还没有被初始化那就初始化它。

  3. 在初次调用java.lang.invoke.MethodHandle实例时,它的执行结果为通过Java虚拟机解析出类型是2(REF_getStatic)、4(REF_putStatic)或者6(REF_invokeStatic)的方法句柄(§5.4.3.5)。

  4. 在调用jdk核心类库中的反射方法时,例如,Class类或java.lang.reflect包。

  5. 在对于类的某个子类的初始化时。

  6. 在它被选定为Java虚拟机启动时的初始类(§5.2)时。

结合上面说的,加载、验证、准备、初始化和卸载这5个阶段的顺序是固定的,需要按这个顺序开始(允许交叉),我们确定了初始化的时机,那么在初始化时或者之前,就要开始加载了。同时还有一点,也就是这个问题涉及到的场景,一个类在验证这个步骤时,会验证A类的字节码,其中可能会涉及到所以来的其他类,根据验证的具体需求,可能需要加载其他类。而这个问题具体的校验过程就是一个方法调用,涉及到类型转换赋值(传入子接口类型,需要转为父接口类型),这种情况下需要加载类型来判断是否可以进行赋值,按理是需要加载赋值左右两边的类型的,但是因为左边类型是接口,被认为都可以赋值,所以没有加载右边类型。

总结来说可能的加载时机为以下几点(不一定全面,是我目前已知的):

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. JVM启动时会预加载一些核心类,比如Object、String等

  3. 这个类被第一次真正使用到时,比如主类因为要调用其main方法,自然需要被加载

  4. 在A类校验阶段,可能需要加载其代码中使用到的B类

  5. 在A类进行某个符号引用的解析时,需要加载对应的B类。比如正在执行A类的一个方法,执行到B.func(),那就需要解析B和func符号引用为直接引用,那自然需要加载B类到方法区中

接下来说下是如何得到上述结论的,首先类加载的流程是Java虚拟机规范中有写的,可以看看。而具体为什么只加载了XXX类,则要调试JVM源码才能知道了。最近因为有看JVM源码,所以编译了并可以进行GDB调试,然后添加条件断点:break  SystemDictionary::load_instance_class if strncmp(class_name._body, "XXX", 3) ==  0,表示在加载XXX类的时候停下来,接着分析调用堆栈:

/ 加载Main类 [Loaded Main from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/]  // 执行Main的初始化方法 Main static block  // 因为要执行Helper.staticMethod()语句,触发加载Helper流程 [Loaded Helper from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/]  // 接着断点停在了加载XXX接口的函数调用上 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  // 查看函数调用栈,分析为什么会需要加载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 // 上面就开始了加载流程了  // 下文分析了这里是在校验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  // 下文分析了这里是因为验证Helper.test(LXXXManager;)V这个方法导致的加载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 // 上面的方法是验证的过程,也就是校验字节码是否正确,是否合法  #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)  // 在类或接口被初始化之前,它必须被链接过,也就是经过验证、准备阶段,且有可能已经被解析完成了。所以上面是链接的流程     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类在运行字节码时,执行到invokestatic指令,对应的语句是Helper.staticMethod() // JVM规范中说明,在执行new,getstatic,putstatic或invokestatic这些指令时,需要确保目标类已经进行初始化流程 // 而初始化流程需要确保目标类已经被加载、验证、准备,所以上面会走到Helper的加载、验证、准备的流程 // 这个堆栈跟踪到的是验证的流程 #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号栈帧中,可以看出的确是正要执行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号栈帧中,可以看出是因为验证Helper.test(LXXXManager;)V这个方法导致的加载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号栈帧中,可以看出是在校验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"

上面分析出了加载是因为验证的流程,具体触发加载的验证代码如下,是验证赋值操作是否可以成功的:

// 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()) {     // 如果赋值语句左边类型是对象,判断是否是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;     }      // 否则需要把左边类型加载进来 <=========================== 加载行为发生在这里     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()) {       // 这里注释说明了,认为接口和Object一样,都可以赋值成功所以返回true       // We treat interfaces as java.lang.Object, including       // java.lang.Cloneable and java.io.Serializable       return true;     } else if (from.is_object()) {       // 否则要把赋值赋予右边的类型也加载进来       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,可以发现两个都会被加载,符合上面这个代码的逻辑。

接着顺便分析一下Helper类加载的堆栈:

  1. // 加载Main类 

  2. [Loaded Main from file:/root/jvm/openjdk/build/linux-amd64-debug/hotspot/outputdir/linux_amd64_compiler2/jvmg/mzb/] 

  3.  

  4. // 执行Main初始化方法 

  5. Main static block 

  6.  

  7. // 断点停在加载Helper类的逻辑上 

  8. Breakpoint 2, SystemDictionary::load_instance_class (class_name=0x7ffff01a60d8, class_loader=..., __the_thread__= 

  9.     0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345 

  10. 1345      instanceKlassHandle nh = instanceKlassHandle(); // null Handle 

  11. (gdb) bt 

  12. #0  SystemDictionary::load_instance_class (class_name=0x7ffff01a60d8, class_loader=..., 

  13.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:1345 

  14. #1  0x00007ffff7578062 in SystemDictionary::resolve_instance_class_or_null (name=0x7ffff01a60d8, 

  15.     class_loader=..., protection_domain=..., __the_thread__=0x7ffff0028000) 

  16.     at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:755 

  17. #2  0x00007ffff7576a17 in SystemDictionary::resolve_or_null (class_name=0x7ffff01a60d8, class_loader=..., 

  18.     protection_domain=..., __the_thread__=0x7ffff0028000) 

  19.     at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:203 

  20. #3  0x00007ffff75765ad in SystemDictionary::resolve_or_fail (class_name=0x7ffff01a60d8, class_loader=..., 

  21.     protection_domain=..., throw_error=true, __the_thread__=0x7ffff0028000) 

  22.     at /root/jvm/openjdk/hotspot/src/share/vm/classfile/systemDictionary.cpp:145 

  23. #4  0x00007ffff70c1915 in constantPoolOopDesc::klass_at_impl (this_oop=..., which=18, 

  24.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:102 

  25. #5  0x00007ffff6fa1f69 in constantPoolOopDesc::klass_at (this=0xfb019e90, which=18, 

  26.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.hpp:366 

  27. #6  0x00007ffff70c2c84 in constantPoolOopDesc::klass_ref_at (this=0xfb019e90, which=65537, 

  28.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/oops/constantPoolOop.cpp:382 

  29. #7  0x00007ffff73817c0 in LinkResolver::resolve_klass (result=..., pool=..., index=65537, 

  30.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:161 

  31. #8  0x00007ffff7385871 in LinkResolver::resolve_pool (resolved_klass=..., method_name=@0x7ffff7fe6638: 0x0, 

  32.     method_signature=@0x7ffff7fe6630: 0x0, current_klass=..., pool=..., index=65537, 

  33.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1062 

  34.  

  35. // Main类在运行字节码时,执行到invokestatic指令,对应的语句是Helper.staticMethod() 

  36. // JVM规范中说明,指令anewarray、checkcast、getfield、getstatic、instanceof、nvokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield和putstatic将符号引用指向运行时常量池,执行上述任何一条指令都需要对它的符号引用的进行解析。 

  37. // 所以这里需要将Main中的运行时常量池中的Helper和staticMethod进行符号解析 

  38. // 符号解析是把符号引用替换为真实引用,自然需要加载Helper类,才能进行替换,所以上面就触发了Helper的加载流程 

  39. #9  0x00007ffff738595b in LinkResolver::resolve_invokestatic (result=..., pool=..., index=65537, 

  40.     __the_thread__=0x7ffff0028000) at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1076 

  41. #10 0x00007ffff738575c in LinkResolver::resolve_invoke (result=..., recv=..., pool=..., index=65537, 

  42.     byte=Bytecodes::_invokestatic, __the_thread__=0x7ffff0028000) 

  43.     at /root/jvm/openjdk/hotspot/src/share/vm/interpreter/linkResolver.cpp:1050 

  44. #11 0x00007ffff7239c58 in InterpreterRuntime::resolve_invoke (thread=0x7ffff0028000, 

  45. ---Type <return> to continue, or q <return> to quit--- 

  46.     bytecode=Bytecodes::_invokestatic) 

  47.     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;   // 解析常量池中的符号引用,会触发加载被调用类的流程 <==================   resolve_pool(resolved_klass, method_name,  method_signature, current_klass, pool, index, CHECK);   // 解析从方法签名解析出方法oop,会触发类的初始化流程 <==================   resolve_static_call(result, resolved_klass, method_name, method_signature, current_klass, true, true, CHECK); }

总结

总结来说可能的加载时机为以下几点(不一定全面,是我目前已知的):

  1. JVM启动时会预加载一些核心类,比如Object、String等

  2. 这个类被第一次真正使用到时,比如主类因为要调用其main方法,自然需要被加载

  3. 在A类校验阶段,可能需要加载其代码中使用到的B类

  4. 在A类进行某个符号引用的解析时,需要加载对应的B类。比如正在执行A类的一个方法,执行到B.func(),那就需要解析B和func符号引用为直接引用,那自然需要加载B类到方法区中

对应A类调用B类的情况,JVM规范中说明,指令anewarray、checkcast、getfield、getstatic、instanceof、nvokedynamic、invokeinterface、invokespecial、invokestatic、invokevirtual、ldc、ldc_w、multianewarray、new、putfield和putstatic将符号引用指向运行时常量池,执行上述任何一条指令都需要对它的符号引用的进行解析,所以需要解析A类中对B类的符号引用,而解析是把符号引用替换为真实引用,所以需要把B类加载到方法区中,这就触发了加载流程。

而B类的链接流程,则是因为JVM规范中说明,在执行new,getstatic,putstatic或invokestatic这些指令时,需要确保目标类已经进行初始化流程,而初始化流程需要确保目标类已经被加载、验证、准备,而加载之前执行过了,所以需要进入验证和准备的流程。而链接中的解析过程不会执行,B类的解析会在执行B类中相关代码时再进行。

上面说的两个过程都是在执行字节码时触发的,比如invokestaic。而B类在验证的过程中,可能又会需要加载其代码中使用到的C类。

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注编程网精选频道,感谢您对编程网的支持。

--结束END--

本文标题: JVM类加载场景的实例分析

本文链接: https://www.lsjlt.com/news/282023.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • JVM类加载场景的实例分析
    JVM类加载场景的实例分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。JVM是Java Virtual  Machine(Java虚拟机)的缩写,J...
    99+
    2023-06-16
  • jvm中类加载过程的示例分析
    这篇文章主要介绍jvm中类加载过程的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!类加载过程:1、加载阶段;2、验证阶段;3、准备阶段,主要是将类变量在方法区进行内存分配并进行初始化;4、解析阶段;5、初始化...
    99+
    2023-06-20
  • JVM的类加载器和双亲委派模式实例分析
    这篇文章主要讲解了“JVM的类加载器和双亲委派模式实例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“JVM的类加载器和双亲委派模式实例分析”吧!类加载器Java虚拟机设计团队有意把类加载...
    99+
    2023-06-29
  • spring boot微服务场景下apollo加载过程实例分析
    本篇内容主要讲解“spring boot微服务场景下apollo加载过程实例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“spring boot微服务场景下apollo加...
    99+
    2023-06-29
  • 详细分析JVM类加载机制
    目录前言1. jvm 的组成2. 类加载1. 加载  2. 链接3. 初始化3. 类加载器引导类加载器(启动类加载器)扩展类加载器应用程序类加载器4. 双亲委派机...
    99+
    2024-04-02
  • JVM分析之类加载机制详解
    目录1、前言2、类加载是什么3、类加载过程3.1 加载3.2 链接3.3 初始化4、总结1、前言 JVM内部架构包含类加载器、内存区域、执行引擎等。日常开发中,我们编写的java文件...
    99+
    2022-11-13
    JVM类加载机制 JVM类加载
  • java类加载的示例分析
    这篇文章将为大家详细讲解有关java类加载的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。1、说明当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过以下三个步骤对该类进行初始化。2、...
    99+
    2023-06-15
  • JVM中加载、链接、初始化的示例分析
    这篇文章主要为大家展示了“JVM中加载、链接、初始化的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JVM中加载、链接、初始化的示例分析”这篇文章吧。基本概念:类加载的过程大致分为三个阶...
    99+
    2023-05-31
    jvm
  • PHP中类加载的示例分析
    这篇“PHP中类加载的示例分析”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“PHP中类加载的示例分析”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们进入主题吧。...
    99+
    2023-06-06
  • java类加载器的示例分析
    这篇文章给大家分享的是有关java类加载器的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Java的优点是什么1. 简单,只需理解基本的概念,就可以编写适合于各种情况的应用程序;2. 面向对象;3. 分布...
    99+
    2023-06-14
  • Android中的类文件和类加载器实例分析
    本篇内容介绍了“Android中的类文件和类加载器实例分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、Java中的类加载器首先花点时间...
    99+
    2023-06-30
  • 一个mysql死锁场景实例分析
    前言 最近遇到一个mysql在RR级别下的死锁问题,感觉有点意思,研究了一下,做个记录。 涉及知识点:共享锁、排他锁、意向锁、间隙锁、插入意向锁、锁等待队列 场景 隔离级别:Repeatable-Rea...
    99+
    2024-04-02
  • jvm crash的实例分析
    这篇文章主要为大家分析了jvm crash的实例分析的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“jvm crash的实例分析”的知识吧。半个月来,新的网站...
    99+
    2023-06-03
  • Unity实现场景加载功能
    unity场景加载分为同步加载和异步加载,供大家参考,具体内容如下 同步加载 loadScene 首先将前置工作做好。 创建一个项目工程,然后创建三个场景 loading00、loa...
    99+
    2024-04-02
  • Java中ClassLoader类加载的示例分析
    这篇文章主要为大家展示了“Java中ClassLoader类加载的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Java中ClassLoader类加载的示例分析”这篇文章吧。双亲委派模型...
    99+
    2023-05-30
    java classloader
  • classloader类加载器基于java类的加载方式的示例分析
    这篇文章主要介绍classloader类加载器基于java类的加载方式的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!基础概念Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中。与...
    99+
    2023-05-31
    classloader java
  • TypeScript工具类Partial和Required的场景分析
    目录场景描述:工具类的使用 PartialPartial 是怎么做的场景描述工具类 Required的简单使用Required是怎么做的?场景描述: 场景描述:一个接口(IPerso...
    99+
    2024-04-02
  • jvm类加载机制怎么实现
    JVM类加载机制是通过类加载器来实现的。类加载器负责在JVM运行时将字节码文件加载到内存中,并将其转换为可执行的类。类加载器主要分为...
    99+
    2024-04-02
  • Vue集成three.js并加载glb、gltf、FBX、json模型的场景分析
    目录先上几个网址安装组件中引入 基本使用 补充 最近刚开始做的一个项目,后面有个模块要通过three.js实现3D的场景,因为之前也没接触过3D这块,就...
    99+
    2024-04-02
  • Oracle DECODE函数实例分析与应用场景
    Oracle DECODE函数是一种非常常用的条件表达式函数,其主要功能是根据指定的条件对表达式的结果进行转换并返回相应的值。DECODE函数的语法如下:DECODE(expr, se...
    99+
    2024-03-08
    分析 oracle decode sql语句
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作