Java字节码常量池深入剖析

摘要:
继续来分析Java字节码,上一节分析了魔数的规则,接下来继续往下分析,其上次总结的规则也一起贴出来:1、使用javap-verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息。如果tag为7表示类常量,而紧接碰上2个字节表示指向全限定名常量项的索引。

继续来分析Java字节码,上一节分析了魔数的规则,接下来继续往下分析,其上次总结的规则也一起贴出来:

1、使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法、类中的方法信息、类变量与成员变量等信息。

2、魔数:所有的.class字节码文件的前4个字节都是魔数,魔数值为固定值:0xCAFEBABE。

3、魔数之后的4个字节为版本信息,前两个字节表示minor version(次版本号),后两上字节表示major version(主版本号),所以这里的版本号为“00 00 00 34”,如下:
Java字节码常量池深入剖析第1张

换算成十进制,表示次版本号为0,主版本号为52,正如咱们用javap -verbose所看到的:

Java字节码常量池深入剖析第2张

那这个版本信息的值有啥意义呢?其中主版本号52代表JDK1.8,而51表示JDK1.7,往前递减依此类推,所以该文件的版本号为:1.8.0,其中1.8为主版本号,而后面的0为次版本号,怎么来验证这一点呢,当然查看java的版本既可嘛,如下:

Java字节码常量池深入剖析第3张

我们知道Java是一个向后兼容的语言,对应到字节码上,如果JVM是1.8的, 则它可以正常加载并运行1.8以及1.8jdk所编译出来的字节码文件, 但是反过来则不行。

4、版本号之后的字节则为常量池(constant pool):紧接着主版本号之后的就是常量池入口。一个Java类中定义的很多信息都是由常量池来维护和描述的,可以将常量池看作是Class文件的资源仓库,比如说Java类中定义的方法和变量信息,都是存储在常量池中,常量池中主要存储两类变量:字面量和符号引用。字面量如文本字符串,Java中声明为final的常量值等,而符号引用如类和接口的全局限定名,字段的名称和描述符,方法的名称和描述符等。其中符号引用在之前的复习总结中也有提到过,回顾一下:
Java字节码常量池深入剖析第4张

【注意】:常量池千万不要理解成它里面只能存不变的常量值,里面也可以有变量相关的信息。

5、常量池的总体结构:Java类所对应的常量池主要由常量池数量与常量池数组这两部分共同构成。常量池数量紧跟在主版本号后面,占据2个字节;常量池数组则紧跟常量池数组之后,常量池数组与一般的数组不同的是,常量池数组中不同的元素的类型、结构都是不同的,长度当然也就不同,但是每一种元素的第一个数据都是一个u1类型,该字节是一个标志位,占据1个字节,JVM在解析常量池时,会根据这个u1类型来获取元素的具体类型。好下面依照该规则来真实查看一下字节码文件:

“常量池数量紧跟在主版本号后面,占据2个字节”,所以我看样一下该字节码文件的常量池数量是?
Java字节码常量池深入剖析第5张

那咱们来看一下javap -verbose输出的结果来验证一下咱们在字节文件中看到的:

Java字节码常量池深入剖析第6张

这是因为常量池数组【又叫常量表】中的元素的个数 = 常量池数 - 1(其中0暂时不使用)。 目的是满足某些常量池索引值的数据在特定情况下需要表达【不引用任何一个常量池】的含义;根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量表中,这个常量就对应null值;所以常量池的索引从1而非0开始。

Java字节码常量池深入剖析第7张

好,那具体常量池中的常量都是如何分配的呢,下面需要了解一个图,非常之重要:

Java字节码常量池深入剖析第8张

可以发现所有常量名都是以CONSTANT开头,以info结尾, 另外下面具体的来说明一下该表怎么来看:

Java字节码常量池深入剖析第9张

其中第一个tag也就是上面我们描述的“每一个元素的第一个数据都是U1类型”,当它的值为1时,表示是CONSTANT_utf8_info类型的常量,而第二个length为u2类型,占两个字节,表示UTF-8编译的字符串长度,而第三个bytes为u1类型点一个字节,表示长度为length的UTF-8编译的字符串内容,也就是根据length来读多少个字节就刚好将这个常量给读完。好接下来继续往下来看:

Java字节码常量池深入剖析第10张

拿CONSTANT_Integer_info来说明,第一个tag占一个字节,其值是3,其需要读4个字节的内容则为该Integer常量的值,其它的Floast、Long、Double都类似,就不多说了。

Java字节码常量池深入剖析第11张

如果tag为7表示类常量,而紧接碰上2个字节表示指向全限定名常量项的索引。

Java字节码常量池深入剖析第12张

这三个常量稍复杂一点,之后会详细介绍,这里先简单的看一下,也就是对于字段和方法都需要先指定类的,然后再到具体的字段或方法。

Java字节码常量池深入剖析第13张

反正不晓得讲什么,云里雾里的,没关系,下一次会完完整整的将字节码中的常量池全部按照上表格的描述规则分析一趟,最终会跟用javap -verbose常量池的输出完全对应上,如下:

Java字节码常量池深入剖析第14张

到那时再回过头来看这张表就会觉得非常之亲切啦~~

免责声明:文章转载自《Java字节码常量池深入剖析》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇使用加密的squid配合stunnel实现HTTP代理psotgres、timescaledb下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

[0] C#软件项目版本号的命名规则及格式介绍

版本控制比较普遍的 3 种命名格式 :  一、GNU 风格的版本号命名格式 :  主版本号 . 子版本号 [. 修正版本号 [. 编译版本号 ]]  英文对照 : Major_Version_Number.Minor_Version_Number[.Revision_Number[.Build_Number]]  示例 : 1.2.1, 2.0, 5.0....

C#学习笔记——常量、字段以及事件

一 常量与字段 (一) 常量   常量总是被视为静态成员,而不是实例成员。定义常量将导致创建元数据。代码引用一个常量时,编译器会在定义常量的程序集的元数据中查找该符号,提取常量的值,并将值嵌入IL中。由于常量的值直接嵌入IL,所以在运行时不需要为常量分配任何内存。此外,不能获取常量的地址,也不能以传递引用的方式传递常量。这些限制意味着,没有很好的跨程序集版...

Microsoft Visual C++ Runtime Library Runtime Error的解决的方法

打开浏览器时,出现Microsoft Visual C++ Runtime Library Runtime Error错误,初步预计是软件冲突,可能有多种出错的方式,我的是浏览器自己主动关闭。 一、 有些时候,在你安装、执行某个软件,可能会得到这样一个错误提示:   Microsoft Visual C++ Runtime Library  ...

SQL Server 2008新特性——更改跟踪

在大型的数据库应用中,经常会遇到部分数据的脱机和多个数据库的合并问题。比如现在有一个全省范围使用的应用程序,每个市都部署了单独的相同的应用程序服务器和数据库服务器,每个月需要将全省所有市的数据全部汇总起来用于出全省的报表,这是一种很常见的数据库合并问题。再比如我们做了一个SmartClient的应用程序,每个客户端都有应用程序和数据库,另外还有一个中心数据...

svn 基础 入门 (Good!)

前言: 软件不能完全解决因沟通造成的问题。 什么是 svnsubversion的版本库是一种文件服务器,它记录每一次改变,每个文件的改变,甚至是目录树本身的改变,例如文件和目录的添加、删除和重新组织。一般情况下,客户端从版本库中获取的数据是文件系统树中的最新数据。但是客户端也具备查看文件系统树以前任何一个状态的能力。举个例子,客户端有时会对一些历史性问题感...

软件版本号规范

原文:https://blog.csdn.net/master_yao/article/details/51274163 1.软件版本阶段说明 oBase版:此版本表示该软件仅仅是一个假页面链接,通常包括所有的功能和页面布局,但是页面中的功能都没有做完整的实现,只是做为整体网站的一个基础架构。 oAlpha版:此版本表示该软件在此阶段主要是以实现软件功能为...