您的位置:首页 > 其它

JVM之类文件结构——上篇(常量池)

2017-04-20 17:17 197 查看
Java语言之所以强大的一个原因就在于它具有跨平台性,Java源文件被编译后的结果不是本地机器码(与机器平台相关联)而是字节码(与机器平台无关),然后由JVM将字节码去加载并执行它。

那么Java的字节码文件到底长什么样?

下面就是我们程序员入门的HelloWorld程序的Class文件

源文件

public class HelloWorld{
public static void main(String[] args){
System.out.println("Hello World");
}
}


Class文件



这里我是用的WinHex软件打开的,把在磁盘上的文件里的二进制转化为十六进制显示出来了

大家可能看的一头雾水,这到底是是什么呀,全是一些数字,完全不知道什么意思,先不要着急,前面的图大概认识一下就行了,接下来我们对照着Java虚拟机规范,就很容易理解每一个数字代表的含义了。

首先我们看前四个字节,也就是下图:



我们可以看到,是0xCAFEBABE,这个称之为魔数,每一个class文件都会有这个东西,这个数字非常有意思,就是咖啡宝贝的意思,非常容易记忆,那么这个数字有什么作用,其实就是一个标志,如果一个文件的头部有了这个0xCAFEBABE这个信息,我就认为你是一个class文件。

接下来的四个字节就是版本号了,前面的两个字节是次版本号,后面的两个字节是主版本号



我们可以看
4000
到,次版本号的值是0x0000,而主版本号是0x0033,这里使用的是大端法(低地址在高位[和人类阅读的习惯相同]),所以,十六进制的0033就是十进制的51,这个对应的编译器(JDK中的javac)版本是JDK 1.7.0,不同版本的编译器编译出来的class文件中的版本号不一样。其实这个信息就表示了你所使用的的编译器版本。【高版本的JDK兼容低版本的JDK】

接下来的两个字节是常量池容量



我们可以看到,常量池容量的值为34(十六进制的0x22),这代表着常量池中有33个常量这里要特别注意不是34个,常量的索引从1开始,索引值范围为1-33,为什么把第0项空出来呢?这是有原因的,这是因为如果有某些指向常量池的索引它想表达“不引用任何常量池项目”,就把它的索引值置为0

接下来的内容就比较重要了,接下来的就是常量池,常量池,顾名思义,里面装的都是常量,都有哪些常量?

总体来说,常量池主要存放了两大类常量:字面量和符号引用



1、什么是字面量?字面量是用于表达源代码中的一个固定值的一个标识,例如:String s = “abc”, 字符串abc就是Java字面量

2、什么是符号引用? 符号引用简单地来说就是一个字符串,该字符串指向了被引用的事物,其实就和人的身份证号一样,你的这个字符串身份证号码就指向了你,比如说Java中一个类中定义了另一个类,那个这个类就具有另一个类的符号引用,当JVM在加载这个类的时候,就会在常量池中获得另一个类的符号引用,在类创建或运行时解析、翻译到具体的内存地址之中。【符号引用和具体的内存布局没有关系】

3、什么是描述符,简单来说就是变量、方法的描述符号,比如 int i = 3; 这个int类型我就用一个符号去描述它

4、什么是全限定名,在Java源文件中可以简单理解为就是包名+类名,比如: com.coderising.jvm.loader.HelloWorld,而在Class文件中,只不过把”.”换成了”/”,如:com/coderising/jvm/loader/HelloWorld

常量池中有许多种常量项,每一个常量项都是一个数据结构。

例如CONSTANT_Class_info



我们结合Class文件来理解一下常量项【u1代表占一个字节,u2代表占两个字节】



虚拟机在读到0x07这个字节的时候,查一下表【虚拟机规范有这个表,在虚拟机规范中规定了所有的常量池结构】,哦,原来这是个CONSTANT_Class_info常量项,然后就会往后读两个字节(虚拟机规范中就规定了这个数据结构),根据虚拟机规范,虚拟机就会知道这两个字节的值代表的是指向一个全限定名[简单地理解为包名+类名]的索引,很显然,这里这个索引的值为2,就是说,我指向了第二个常量项(其实就是下一个常量项)注意:这里的 07 00 02 这三个字节是一个整体,这三个字节符合CONSTANT_Class_info数据结构,这个整体是一个常量项,并且是第一个常量项。

下来我们再看看在常量池用频繁出现的CONSTANT_UTF8_info常量项,它的数据结构如下:



结合Class文件理解一下:



注意:第二个常量项是图中所有浅蓝色的部分,其中包含tag(值为0x01)、length(值为0x0024)和后面的36个字节。这和CONSTANT_UTF8_info的数据结构是对应的。这个时候我们可以看到,其实第一个CONSTANT_Class_info的索引值是指向第二个常量项的,这个常量项的内容其实就是com/coderising/jvm/loader/HelloWorld,其实就是包名+类名。我们可以通过这种查表的方式,找到Class文件中的所有数字所代表的含义,详情请参考《深入理解Java虚拟机》第六章P172中的表6-6。

我们看一看最后一个常量项:



最后一个常量项一般为这个Java源文件的名字。很显然这是一个UTF8Info数据结构。

大家可以看到,Java源文件中的所有的字面量和符号引用都被存在了这个Class文件之中了

比如说,符号引用: com.coderising.jvm.loader.HelloWorld,还有我们在程序中要输出的字面量“Hello World”

下来我们用javap -verbose 类名来输出一下我们这个HelloWorld.class



好了,常量池就聊到这里

本文参考《深入理解Java虚拟机》周志明 著
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: