Class文件解读(二)

接上篇:https://my.oschina.net/u/3345762/blog/875767

常量池(constant_pool_count、constant_pool)

        Class文件中紧接着主次版本号之后的是常量池入口,常量池可以理解为Class文件中的资源仓库,因为Class文件中的绝大部分的字面量及符号引用就存储与常量池中。它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表类型数据项目。

    由于常量池(constant_pool)中常量的数量是不固定的,因此使用了一个常量池容量计数值(constant_pool_count)来表示某一Class文件中具体常量池中的常量数目,这里需要知道常量池的容量计数是从1开始而不是0,而其他集合类型,如:接口索引集合、字段表集合、方法表集合等容量计数都是从0开始。从图一中可以看出该Class文件中常量池容量值偏移量为00000008,值为Ox19,转为十进制也就是25,则可算得该Class文件中的常量池容量为24(25-1)。

                        

                                                                   图1   常量池结构

     上文中有阐述到常量池中主要存放的是两大类常量:字面量(Literal)及符号引用(Symbolic References),字面量比较接近于java语言中常量的概念,如文本字符串(类名、方法名等)、声明为final的常量值等,而符号引用则属于编译原理方面的概念,包括下面三类常量:

  • 类和接口的全限定名(Fully Qualified Name)
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符

        java代码在进行javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保持各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池中获取对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。

        常量池中每一项常量都是一个表,在JDK1.7之前共有11种结构各不相同的表结构数据,在JDK1.7中为了更好地支持动态语言调用(动态语言区别于静态语言的一个重要特征是:动态语言的类型检查的主体过程在运行期而不是在编译期),增加了(CONSTANT_MethodHandle_info、CONSTANT_MethodType_info和CONSTANT_InvokeDynamic_info)。

        这14种表都有一个共同的特点,就是表开始的第一位是一个u1类型的标志位(tag,取值见下表中标志列),代表当前这个常量属于哪种常量类型。

                                                                        表1  常量池的项目类型

                   

        之所以说常量池是最繁琐的数据,是因为这14种常量类型各自均有自己的结构。回头看图1中常量池的第一项常量,它的标志位(偏移地址:Ox0000000A)是Ox0A,查表1的标志列发现这个常量属于CONSTANT_Methodref_info类型,此类型的常量表示类中方法的符号引用。CONSTANT_Methodref_info的结构见表2。

                                                表2  CONSTANT_Methodref_info型常量的结构

                                       

      tag是标志位,用于区分常量类型;class_index是一个索引值,它指向声明方法的类的描述符CONSTANT_class_info的索引项,这里class_index的值(偏移地址:Ox0000000B)为Ox0004,也即是指向了常量池中的第四项常量,结合表1常量池的项目类型在图一中找到第四位常量,偏移地址为Ox00000010,值为Ox07,得到第四位常量是类型为CONSTANT_class_info类型的常量,该类型常量的结构见表3。

                                                    表3  CONSTANT_class_info型常量的结构

                                         

此常量代表了这个类(或接口)的全限定名,这里name_index值(偏移地址Ox00000018)为Ox0018,也即是指向了常量池中的第二十四项常量,这里就不一个一个去数啦,后面有工具可以直接用,最终会发现第二十四项常量的类型为CONSTANT_utf8_info类型(在常量池中该常量类型基本是最多的)。该类型常量结构见表4。

                                                   表4  CONSTANT_utf8_info型常量的结构

                                                  

tag值为7(见表1),length表示这个使用UTF-8编码的字符串占用的字节数,它后面紧跟着的长度为length字节的连续数据是一个使用UTF-8缩略编码的字符串,这里使用缩略编码而不是普通的UTF-8编码,主要是为了节省空间,体现出class文件的“精湛”。值得一提的是,由于Class文件中方法、字段等都需要引用CONSTANT_Utf8_info型常量来描述名称,所以length的最大长度也就是java中方法、字段名的最大长度。而这里的最大长度就是u2能表示的最大值65535,所以当java程序中定义了超过64KB英文字符的变量或方法名,将会无法编译。对于在本例中第二十四位常量里面表示的常量值为方便起见我们借助javap工具进行查看(当然也可以通过class文件中的对应字节算出来),如下图:

                                                

                                                            图2  常量池中具体常量

上图详细列出了示例代码编译后的Class文件中,常量池里面的具体常量类型、常量值及相应的符号引用,从中我们可以看出该Class文件中的第二十四位常量值为java/lang/Object。我们再回过头来看常量池中第一位常量类型为CONSTANT_Methodref_info的常量,该常量里的NameAndType_index可从图2中看到它分别指向了第十位和第十一位常量,第十位常量<init>是方法名,而第十一位常量是该方法的方法描述符,具体的方法描述符的描述规则会在下文中阐述。

       至此我们完整地将本例常量池中的第一位常量类型为CONSTANT_Methodref_info的常量含义,一步一步地阐述清楚,对于常量池中的其他常量亦可用上述的方法进行阅读理解。为方便读者进一步了解常量池,下面给出14种常量类型的结构:

           

          行文至此,Class文件结构中的常量池相关内容已介绍完毕,希望读者能有所获。

下篇:https://my.oschina.net/u/3345762/blog/889512

 

转载于:https://my.oschina.net/u/3345762/blog/880152

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值