【问题标题】:Where the resolved reference(that means direct memory address against symbolic reference) stored in JVM after resolution?解析后在 JVM 中存储的解析引用(即针对符号引用的直接内存地址)在哪里?
【发布时间】:2018-12-04 11:57:31
【问题描述】:

我研究过 JVM(尤其是 JDK 8 版本),在研究类链接的同时,我还没有弄清楚从解析中的符号引用确定的直接内存地址在哪里。

解析有好几种,比如类型(类/接口)、字段、方法等,但我只是做了一个类的例子来简单说明。

在JVM规范中,有一些词。

5.1 运行时常量池 Java 虚拟机维护每个类型的常量池(第 2.5.5 节),这是一种运行时数据结构,可用于传统编程语言实现的符号表的许多目的。 类或接口的二进制表示形式的 constant_pool 表(第 4.4 节)用于在类或接口创建时(第 5.3 节)构建运行时常量池。 运行时常量池中的所有引用最初都是象征性的。

规范说,所有的引用一开始都是符号引用。

这是一个示例 Main 类。

public class Main {
    public static void main(String[] args) {
        Object obj = new Object();
    }
}

这是 Main 类的常量池信息。

Constant pool:
#1 = Methodref          #2.#12         // java/lang/Object."<init>":()V
#2 = Class              #13            // java/lang/Object
#3 = Class              #14            // Main
#4 = Utf8               <init>
#5 = Utf8               ()V
#6 = Utf8               Code
#7 = Utf8               LineNumberTable
#8 = Utf8               main
#9 = Utf8               ([Ljava/lang/String;)V
#10 = Utf8               SourceFile
#11 = Utf8               Main.java
#12 = NameAndType        #4:#5          // "<init>":()V
#13 = Utf8               java/lang/Object
#14 = Utf8               Main
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method     java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class java/lang/Object
         3: dup
         4: invokespecial #1                  // Method java/lang/Object."<init>":()V
         7: astore_1
         8: return
      LineNumberTable:
        line 3: 0
        line 4: 8
}
SourceFile: "Main.java"

4.4.1 CONSTANT_Class_info 结构
CONSTANT_Class_info 结构用于表示一个类或一个 > 接口:
CONSTANT_Class_info {
u1 标签;
u2 name_index;
}

这里,Object 类在 Main 类的 main 方法中被引用。在 Main 类中,从不引用 Object 类。(当命令 java Main 刚刚执行时;)这意味着 Main 的常量池中的 Object Class entry(here, #2: CONSTANT_Class_info structure.) 具有 name_index #13。 #13 是包含 Object 类名称的 CONSTANT_Utf8_info 结构,#13 是 Object 类的符号引用。(老实说,我可能不确定这个 Utf8 常量池条目是 #2(Object 的类池条目)的符号引用)

当 JVM 的执行引擎只执行一个字节码,该字节码具有 Object 类的引用(在这个类中,0: new #2),#2 引用 #13(符号引用)。所以,需要解析为JVM中Method Area上Object Class的直接地址。并发生类解析。

问题来了。 我已经阅读并搜索了 JVM 规范、博客、文章,但我找不到 JVM 中符号引用存储的已解析直接内存地址。

我在blog中找到了一些信息,上面写着,

绑定是符号引用被直接引用替换所标识的字段、方法或类的过程,这只会发生一次,因为符号引用被完全替换了。

它说,替换。 在#2常量池入口中,Object类的符号引用存储在CONSTANT_Class_info结构的name_index(u2 type)字段中。

name_index字段的值是否改为Object Class的直接内存地址(可能在Method Area中Object clsas的运行时常量池中)????

如果不是,直接地址存放在哪里?

请给我答案。谢谢。

【问题讨论】:

  • 当然,你不会在规范中找到这个,因为这完全取决于特定的 JVM 实现如何做到这一点。但是可以说,常量池的运行时表示不太可能与存储在磁盘上的类文件中的完全一样。所以基于磁盘存储格式讨论运行时行为是没有意义的。
  • @Holger 我明白了。我没有考虑常量池的运行时表示。但是,对于您以“毫无意义的讨论”结束您的话,我感到有些抱歉。我只是以为你知道特定供应商行为的解决方案,但你只是结束......我会找到它。
  • 你误解了这个短语。在错误的基础上讨论是没有意义的,但是您可以解决您的问题以纠正起点。这不是终结短语,您的问题尚未结束。

标签: java jvm resolution dynamic-linking symbolic-references


【解决方案1】:

规范没有说明 JVM 将已解析的常量池条目存储在何处。这是特定于实现的细节。

在 HotSpot JVM 中,常量池位于元空间中。它由两个相关的数组组成:一个标签数组和一个值数组。标签描述了相应值的类型。但这些标签JVMS §4.4 中定义的标签相同。 JVM 在类文件解析阶段用自己的标签填充常量池。

有 4 种不同类型的常量池条目表示对 Java 类的引用:

  • JVM_CONSTANT_ClassIndex 最初包含指向具有类名的常量池 Utf8 条目的整数索引。
  • JVM_CONSTANT_UnresolvedClass。常量池初始内容完全加载后,JVM将JVM_CONSTANT_ClassIndex标签改为JVM_CONSTANT_UnresolvedClass,并将对应的cp条目替换为符号名。
  • JVM_CONSTANT_UnresolvedClassInErrorJVM_CONSTANT_UnresolvedClass 含义相同,但表示类解析尝试失败。
  • JVM_CONSTANT_Class 是已解析类的内部表示的原始地址。

所以,您的猜测是正确的:在恒定池解析期间,HotSpot JVM 修改了 cp 条目并更改了相应的 cp 标签。也就是说,JVM_CONSTANT_UnresolvedClass 变成了JVM_CONSTANT_Class,符号引用被替换为同一个常量池值数组中的直接地址。

你可以在ConstantPool::klass_at_impl找到实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-11
    • 1970-01-01
    • 2014-02-13
    • 1970-01-01
    • 2015-03-23
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多