java字节码文件里面的常量池

旧巷老猫 提交于 2021-02-19 12:06:58

1. 常量池在class文件的什么位置?

2. 常量池的里面是怎么组织的?

3. 常量池项 (cp_info) 的结构是怎样的?

JVM虚拟机规定了不同的tag值和不同类型的字面量对应关系如下:

类型 标志 描述 结构
CONSTANT_utf8_info 1 UTF-8编码的字符串(字面) u1,u2,bytes[u2]
CONSTANT_Integer_info 3 int整形字面量(字面) u1,u4
CONSTANT_Float_info 4 float浮点型字面量(字面) u1,u4
CONSTANT_Long_info long长整型字面量(字面) u1,u4,u4
CONSTANT_Double_info double双精度浮点型字面量(字面) u1,u4,u4
CONSTANT_Class_info 类或接口的全限定名(引用) u1,u2
CONSTANT_String_info String类型的常量对象(引用) u1,u2
CONSTANT_Fieldref_info 类中的字段(引用) u1,u2,u2
CONSTANT_Methodref_info 10 类中的方法(引用) u1,u2,u2
CONSTANT_InterfaceMethodref_info 11 类所实现的接口方法(引用) u1,u2,u2
CONSTANT_NameAndType_info 12 字段和方法的名称和类型(引用) u1,u2,u2
CONSTANT_MethodHandle_info 15 表示方法句柄(引用)  
CONSTANT_MothodType_info 16 标志方法类型(引用)  
CONSTANT_Dynamic_info 17 u1,u2,u2
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点(引用)  
CONSTANT_Module_info 19 u1,u2
CONSTANT_Package_info 20 u1,u2

4. 常量池 能够表示那些信息?

5. int和float数据类型的常量在常量池中是怎样表示和存储的?

  • ( ----介绍 常量池项 CONSTANT_Integer_info, CONSTANT_Float_info)

Java语言规范规定了 int类型和Float 类型的数据类型占用 4 个字节的空间。那么存在于class字节码文件中的该类型的常量是如何存储的呢?相应地,在常量池中,将 int和Float类型的常量分别使用CONSTANT_Integer_info和 Constant_float_info表示,他们的结构如下所示:

6. long和 double数据类型的常量在常量池中是怎样表示和存储的?

  • (----介绍 常量池项 CONSTANT_Long_info, CONSTANT_Double_info)

7. String类型的字符串常量在常量池中是怎样表示和存储的?

  • ( ----介绍 常量池项 CONSTANT_String_info,CONSTANT_Utf8_info)

如上图所示的结构体,CONSTANT_String_info结构体中的string_index的值指向了CONSTANT_Utf8_info结构体,而字符串的utf-8编码数据就在这个结构体之中。如下图所示:

请看一例,定义一个简单的StringTest.java类,然后在这个类里加一个"JVM原理" 字符串,然后,我们来看看它在class文件中是怎样组织的。


public class StringTest {
	private String s1 = "JVM原理";
	private String s2 = "JVM原理";
	private String s3 = "JVM原理";
	private String s4 = "JVM原理";
}


将Java源码编译成StringTest.class文件后,在此文件的目录下执行 javap -v StringTest 命令,会看到如下的常量池信息的轮廓: 在面的图中,我们可以看到CONSTANT_String_info结构体位于常量池的第#15个索引位置。而存放"Java虚拟机原理" 字符串的 UTF-8编码格式的字节数组被放到CONSTANT_Utf8_info结构体中,该结构体位于常量池的第#16个索引位置。上面的图只是看了个轮廓,让我们再深入地看一下它们的组织吧。请看下图:

8. 类文件中定义的类名和类中使用到的类在常量池中是怎样被组织和存储的?

  • ----介绍 常量池项 CONSTANT_Class_info

Tips:类的完全限定名和二进制形式的完全限定名 在某个Java源码中,我们会使用很多个类,比如我们定义了一个 ClassTest的类,并把它放到com.jvm 包下,则 ClassTest类的完全限定名为com.jvm.ClassTest,将JVM编译器将类编译成class文件后,此完全限定名在class文件中,是以二进制形式的完全限定名存储的,即它会把完全限定符的"."换成"/" ,即在class文件中存储的 ClassTest类的完全限定名称是"com/jvm/ClassTest"。因为这种形式的完全限定名是放在了class二进制形式的字节码文件中,所以就称之为 二进制形式的完全限定名。

9. 类中引用到的field字段在常量池中是怎样描述的?

  • ----介绍 常量池项 CONSTANT_Fieldref_info, CONSTANT_Name_Type_info

一个CONSTANT_Fieldref_info常量池项与其他项的关联关系

实例解析: 现在,让我们来看一下Person类中定义的namefield字段在常量池中的表示。通过使用javap -v Person会查看到如下的常量池信息:

请读者看上图中namefield字段的数据类型,它在#6个常量池项,以UTF-8编码格式的字符串“Ljava/lang/String;” 表示,这表示着这个field 字段是java.lang.String 类型的。关于field字段的数据类型,class文件中存储的方式和我们在源码中声明的有些不一样。请看下图的对应关系:

注意: 如果我们在类中定义了field 字段,但是没有在类中的其他地方用到这些字段,它是不会被编译器放到常量池中的。读者可以自己试一下。(当然了,定义了但是没有在类中的其它地方引用到这种情况很少。) 只有在类中的其他地方引用到了,才会将他放到常量池中。

10. 类中引用到的method方法在常量池中是怎样被描述的?

  • ----介绍 常量池项 CONSTANT_Methodref_info

方法描述符的组成

11. 类中引用到某个接口中定义的method方法在常量池中是怎样描述的?

  • ----介绍 常量池项 CONSTANT_InterfaceMethodref_info

CONSTANT_InterfaceMethodref_info结构体和上面介绍的CONSTANT_Methodref_info 结构体很基本上相同,它们的不同点只有:

  1. CONSTANT_InterfaceMethodref_info 的tag 值为11,而CONSTANT_Methodref_info的tag值为10;

  2. CONSTANT_InterfaceMethodref_info 描述的是接口中定义的方法,而CONSTANT_Methodref_info描述的是实例类中的方法;

12. CONSTANT_MethodType_info 、 CONSTANT_MethodHandle_info 、 CONSTANT_InvokeDynamic_info

CONSTANT_MethodType_info,CONSTANT_MethodHandle_info,CONSTANT_InvokeDynamic_info 这三项主要是为了让Java语言支持动态语言特性而在Java 7 版本中新增的三个常量池项,只会在极其特别的情况能用到它,在class文件中几乎不会生成这三个常量池项。 其实我花了一些时间来研究这三项,并且想通过各种方式生成这三项,不过没有成功,最后搞的还是迷迷糊糊的。从我了解到的信息来看,Java 7对动态语言的支持很笨拙,并且当前没有什么应用价值,然后就对着三项的研究先放一放了。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!