类加载器解析、栈动态链接、符号引用和直接引用
符号引用和直接引用
-
符号引用(Symbolic Reference): 符号引用是一种相对抽象的引用形式,通常以字符串的形式表示。例如,在字节码中,一个类、方法或字段的引用可能被表示为一个符号,例如类名、方法名或字段名。这些引用没有直接指向内存中的某个位置,而只是某种“符号”。
例如:
- 类名
"com.example.MyClass"
- 方法名
"myMethod()V"
(V
代表返回void
) - 字段名
"myField"
- 类名
-
直接引用(Direct Reference): 直接引用是指内存中的具体地址。经过解析阶段后,符号引用会被替换为指向具体类、方法或字段的内存地址,JVM能够通过这些地址直接访问或调用。
类加载器解析
注意如下所说的运行时再进行解析都指的是栈的动态链接
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用限定符 7 类符号引用进行。
Class文件中编译期生成的各种字面量(Literal)和符号引用(Symbolic Reference)储存在 **常量池表(Constant Pool Table)**中 。
常量池表会在类加载后存放到方法区的运行时常量池中。
在解析阶段,JVM会对字节码中的以下几种符号引用进行解析:
- 类或接口的解析:
- 当字节码中引用某个类或接口时,JVM需要将类名或接口名的符号引用解析为具体的类或接口的内存地址。
- 例如,
Lcom/example/MyClass;
是对类MyClass
的符号引用,在解析时需要将其转换为内存中的类对象的引用。
- 字段的解析:
- 字节码中对某个字段的访问会使用符号引用,例如
MyClass.myField
。JVM在解析时会将这个符号引用解析为具体的字段在内存中的位置,确保后续的字段读取和写入能够直接访问该字段。 - 这包括静态字段和实例字段的解析。
- 字节码中对某个字段的访问会使用符号引用,例如
- 方法的解析:
- 字节码中的方法调用,如
invokevirtual
、invokestatic
等指令,都会使用符号引用表示方法。在解析过程中,JVM需要将这些方法符号引用解析为具体的函数地址,确保运行时能够正确调用该方法。 - 特别是对于静态方法和非虚方法(如
private
方法或final
方法),JVM会在类加载时完成解析。而对于虚方法(如普通的实例方法),在解析时可能只解析到类的方法表,具体调用的实现则在运行时通过动态链接完成。
- 字节码中的方法调用,如
- 接口方法的解析:
- 接口方法的解析类似于普通方法调用,但解析的过程稍微复杂,因为接口的实现类是在运行时确定的。在解析时,JVM需要解析接口方法符号引用,确保后续的动态绑定能够正确找到实际的实现类。
解析的时机
解析过程通常是在类加载期间完成,但在某些情况下,解析可能会被延迟到运行时。JVM可以在类加载时立即解析所有符号引用,也可以选择在首次使用某个符号引用时(例如,第一次调用某个方法或访问某个字段时)才进行解析。
- 类加载时解析:静态方法、非虚方法和静态字段的解析通常会在类加载期间完成。
- 运行时解析:虚方法、接口方法的解析则在运行时延迟进行,因为这些方法的具体调用目标在运行时才确定。
解析的细节与优化
在解析过程中,JVM会根据不同的符号引用类型执行不同的解析操作。以下是一些常见的解析操作及其细节:
-
类解析(Class Resolution):
- JVM会根据符号引用中的类名,在当前类的常量池中查找类的定义。如果该类尚未加载,则触发类加载机制,将其加载到内存中。
- 如果符号引用的类是通过类加载器加载的,解析过程中还会检查类加载器是否能正确找到该类,并确保类的定义符合规范。
-
字段解析(Field Resolution):
- 字段的符号引用包括字段所属的类名和字段的名称与描述符。JVM会根据类的常量池查找该字段,如果找到该字段的定义,就将其符号引用解析为内存地址。
- 字段解析的复杂性在于继承链:如果当前类没有找到该字段的定义,JVM会沿着继承树向上查找,直到找到字段定义为止。如果整个继承树中都找不到该字段,则抛出
NoSuchFieldError
。
-
方法解析(Method Resolution):
-
方法解析分为两类:
静态方法和虚方法
- 对于静态方法和非虚方法(如
private
方法或final
方法),它们的符号引用会在类加载时解析为具体的内存地址。 - 对于虚方法,解析时通常只解析到类的方法表(vtable),具体的调用目标在运行时通过对象的实际类型确定。这个过程依赖于虚方法表的动态链接机制。
- 对于静态方法和非虚方法(如
-
-
接口方法解析(Interface Method Resolution):
- 接口方法的解析更复杂,因为实现接口的类是在运行时确定的。在解析过程中,JVM会查找接口中定义的方法,并在运行时根据实际的实现类动态确定调用哪个实现。