吴晟的个人主页

My Profile and Experience

View project on GitHub

探秘JVM内部结构,第三部分

吴晟sky-walking APM

反馈文章问题

回到第二部分…

Run Time Constant Pool 续

如果你编译下面这个类:

package org.jvminternals;

public class SimpleClass {

    public void sayHello() {
        System.out.println("Hello");
    }
}

会生成如下所示的常量池:

Constant pool:
   #1 = Methodref          #6.#17         //  java/lang/Object."<init>":()V
   #2 = Fieldref           #18.#19        //  java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #20            //  "Hello"
   #4 = Methodref          #21.#22        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #23            //  org/jvminternals/SimpleClass
   #6 = Class              #24            //  java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               Lorg/jvminternals/SimpleClass;
  #14 = Utf8               sayHello
  #15 = Utf8               SourceFile
  #16 = Utf8               SimpleClass.java
  #17 = NameAndType        #7:#8          //  "<init>":()V
  #18 = Class              #25            //  java/lang/System
  #19 = NameAndType        #26:#27        //  out:Ljava/io/PrintStream;
  #20 = Utf8               Hello
  #21 = Class              #28            //  java/io/PrintStream
  #22 = NameAndType        #29:#30        //  println:(Ljava/lang/String;)V
  #23 = Utf8               org/jvminternals/SimpleClass
  #24 = Utf8               java/lang/Object
  #25 = Utf8               java/lang/System
  #26 = Utf8               out
  #27 = Utf8               Ljava/io/PrintStream;
  #28 = Utf8               java/io/PrintStream
  #29 = Utf8               println
  #30 = Utf8               (Ljava/lang/String;)V

常量池包含以下类型:

  • Integer:4 bytes的整数常量
  • Long:8 bytes的长整形常量
  • Float:4 bytes浮点
  • Double:8 bytes浮点
  • String:一个指向另一个Utf8实体的引用
  • Utf8:一个使用UTF-8编码的字符串常量
  • Class:一个指向另一个Utf8实体的引用,那个实体中存储类的全名,在动态链接时使用(查看Dynamic Linking章节)。
  • NameAndType:以冒号分隔的两个值,每一个都指向常量池的一个实体。第一个值指向一个Utf8的实体,代表方法名或字段名;第二个值指向一个Utf8实体,代表类型,包含字段的全名,如果是方法,还会包含完整的方法签名和参数名。
  • Fieldref,Methodref,InterfaceMethodref:用点分隔的两个值,每一个都指向常量池的一个实体。第一个指向一个Class实体,第二个指向一个NameAndType实体

Exception Table

异常表中,每一个异常存储一下信息:

  • 起始点
  • 结束点
  • 异常处理逻辑的PC偏移量
  • 常量池索引值,指向异常类

如果一个定义类try-catch或者try-finally异常处理,则异常表会被创建。其中包含每一个异常处理块和finally块,以及他们对应处理哪种类型的异常以及处理代码的位置。

当异常发生时,JVM在当前方法中寻找匹配的异常处理块,如果不存在,则强制弹出当前的栈帧,并在新的父级栈帧(调用发生异常的方法的父级方法)中重新抛出异常。如果当前线程中的所有栈帧都不包含处理块,则线程终止退出。同时,如果此线程是最后一个非守护线程(如:main线程),则会导致JVM的退出。

Finally块匹配所有的异常,无论异常何时被抛出,都会执行。为了保证没有异常抛出时,Finally块依然会执行,程序会在return代码执行后,调到finally代码块。

Symbol Table

Hotspot JVM有存在在permanent区的一个符号表。符号表是一个Hashtable,建立引用指针和各种符号引用的映射关系(例如:Hashtable<Symbol*, Symbol>),同时包含为每一个类创建的,一个指向类的运行时常量池的指针。

当从符号表中移除一个符号时,需要使用到引用计数器。例如,当一个类被卸载时,其运行时常量池中保存的所有符号的引用计数递减。当符号表中,一个符号的引用计数器降为0,符号表就知道,这个符号没有被任何人使用,那么这个符号可以从符号表中被移除。对于符号表和字符串表(见下节),所有条目都以规范化形式保存,以提高效率,并确保每个条目只存储一次。

Interned Strings (String Table)

Java语言规范要求,当一个字符串,即包含相同Unicode编码的字符的集合,完全相同时,必须指向同一个String实例。另外,String实例的String.intern()方法返回的引用,必须和该字符串值的引用保持一致。如,下面的表达式,始终是true:

("j" + "v" + "m").intern() == "jvm"

Hotspot JVM中,字符串被保存在字符串表中,通过一个Hashtable建立对象引用和符号的关系(例如:Hashtable<oop, Symbol>)。这个表被保存在permanent区中。对于符号表(见上节)和字符串表,所有条目都以规范化形式保存,以提高效率,并确保每个条目只存储一次。

当类被加载时,字符串常量通过编译器动态的加入到字符串表和符号表中。另外String实例可以通过显示的调用String.intern()被加入到字符串表中。当String.intern()被调用时,如果此字符串已经存在于字符串表中,则直接返回其引用,如果不存在,则将其加入到字符串表,并返回引用。


返回吴晟的首页     英文版