[JVM]理解Class文件(1):手动解析常量池

引言

class文件是Java虚拟机提供的语言无关性的基石,如果要深入地了解虚拟机,那么必须学习class文件是如何解析的。

Java虚拟机提供的语言无关性

而常量池是class文件结构中第一个出现的表类型数据项目,也是Class文件中最烦琐的数据,理解了常量池的解析过程,Class文件其它字段的分析也就迎刃而解了。

Class文件结构

ClassFile结构

Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,包含两种数据类型

  • 无符号数:类型有u1、u2、u4、u8分别代表1个字节、2个字节、4个字节、8个字节
  • : 表是多个无符号数或者其他表组合而成的复合数据,如:cp_info constant_pool[constant_pool_count-1]就是一个表结构的数据。

每一个Class文件对应于一个如下所示的ClassFile结构体:

ClassFile {
    u4 magic; 
    u2 minor_version; 
    u2 major_version; 
    u2 constant_pool_count; 
    cp_info constant_pool[constant_pool_count-1];
    u2 access_flags; 
    u2 this_class; 
    u2 super_class; 
    u2 interfaces_count; 
    u2 interfaces[interfaces_count]; 
    u2 fields_count; 
    field_info fields[fields_count]; 
    u2 methods_count; 
    method_info methods[methods_count]; 
    u2 attributes_count; 
    attribute_info attributes[attributes_count]; 
}

示例java代码

将下面这段代码编译生成class,用作后续分析。

public class TestClass {
    private static final String TEST = "test string";
}

示例class文件

将生成的class文件用UltraEdit打开,可以清楚地看到Java编译后生成的字节码,我们要解析的内容也就是这些字节码。

00000000h: CA FE BA BE 00 00 00 33 00 12 0A 00 03 00 0E 07 ; 
00000010h: 00 0F 07 00 10 01 00 04 54 45 53 54 01 00 12 4C ; 
00000020h: 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 ; 
00000030h: 3B 01 00 0D 43 6F 6E 73 74 61 6E 74 56 61 6C 75 ; 
00000040h: 65 08 00 11 01 00 06 3C 69 6E 69 74 3E 01 00 03 ; 
00000050h: 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E ; 
00000060h: 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 0A 53 ;
00000070h: 6F 75 72 63 65 46 69 6C 65 01 00 0E 54 65 73 74 ;
00000080h: 43 6C 61 73 73 2E 6A 61 76 61 0C 00 08 00 09 01 ; 
00000090h: 00 09 54 65 73 74 43 6C 61 73 73 01 00 10 6A 61 ; 
000000a0h: 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 ;  
000000b0h: 0B 74 65 73 74 20 73 74 72 69 6E 67 00 21 00 02 ;  
000000c0h: 00 03 00 00 00 01 00 1A 00 04 00 05 00 01 00 06 ;  
000000d0h: 00 00 00 02 00 07 00 01 00 01 00 08 00 09 00 01 ;  
000000e0h: 00 0A 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 ; 
000000f0h: 00 01 B1 00 00 00 01 00 0B 00 00 00 06 00 01 00 ; 
00000100h: 00 00 02 00 01 00 0C 00 00 00 02 00 0D          ; 

魔数和版本号

在解析常量池之前,先来看下Class文件中最开始的两个字段

  • 魔数
    魔数是用来标识这支文件是能被虚拟机所接受的Class文件。魔数值固定为0xCAFEBABE,不会改变。

由于文件扩展名是可以随意修改的,因此,很多文件都会使用魔数作为文件标识,如gif或者jpeg等在头文件中都存在魔数。

  • 版本号
    class文件版本由副版本号+主版本号组成(minor_version + major_version),通过版本号可以知道对应编译器的版本。下图列出了每个JDK版本对应的十六进制版本号
class文件版本号
  • 示例解析
    头4个字节为魔数CA FE BA BE,紧接着为版本号00 00 00 33,对照上面的表格,可以知道编译器的版本为JDK 1.7.0
    图1.魔数、版本号和常量池计数器

常量池

存放内容

常量池存放的内容如下:

  • 字面量

    • 文本字符串、声明为final的常量值
  • 符号引用

    • 类和接口的全限定名
    • 字段的名称和描述符
    • 方法的名称和描述符

常量池数据结构

要解析常量池的数据,先得看下它的数据结构,这些数据结构都是在 Java虚拟机规范(Java SE 7).pdf里面定义的。

  • ClassFile

在class文件中,常量池入口紧接在主版本后之后, constant_pool_count表示常量的个数,constant_pool是存放的是所有常量信息的表项,存放的个数为constant_pool_count - 1constant_pool可以将理解为一个数组,其中的每一项代表一个常量。

由“图1.魔数、版本号和常量池计数器”所示constant_pool_count值为18,由于常量池容量计数是从1开始的,0用作表示“不引用任何对象”,因此,常量个数为17个(constant_pool_count - 1)

ClassFile {
    ... ...
    u2 constant_pool_count;
    cp_info constant_pool[constant_pool_count-1];
    ... ...
}

  • cp_info

    要想知道constant_pool里面是怎样存放常量信息的,就需要先看cp_info这个结构体的内容

cp_info { 
    u1 tag; 
    u1 info[]; 
}

其中,tag表示常量的类型info[]代表tag类型所属的表项

tag值对应的类型如下表:

常量池tag

以开头的class文件为例,紧接着常量池计数器后的"0A"是第一个常量的tag,"0A"对应十进制值为10,结合上表,得出第一个常量类型为CONSTANT_Methodref

0A.png


  • CONSTANT_Methodref_info

CONSTANT_Methodref对应的数据结构为CONSTANT_Methodref_info,也就是说constant_pool[1]中的u1 info[]CONSTANT_Methodref_info

  • tag表示当前数据类型CONSTANT_Methodref_info,说明这个常量是一个方法

  • class_index表示引用这个方法的对象在常量池数组的中索引,说明constant_pool[class_index]存放的就是调用该方法的对象名称

  • name_and_type_index指的该方法在常量池数组的中索引,即constant_pool[name_and_type_index]存放着该方法的名称

CONSTANT_Methodref_info { 
    u1 tag; 
    u2 class_index; 
    u2 name_and_type_index; 
}

根据上面的描述,我们接着解析"0A"后面的字段,由CONSTANT_Methodref_info数据结构可知,"0A"往后再取2个u2类型数据的长度就截止了,其中class_index = 3, name_and_type_index = 14

假设我们已经将后面的常量池字段全部解析完成了(文末附有整个常量池解析结构),可以得出constant_pool[3] = "java/lang/Object" ,constant_pool[14] = " "<init>":()V "

因此,第一个常量constant_pool[1]为:java/lang/Object."<init>":()V

CONSTANT_Methodref_info

  • 解析第二个常量

类型为CONSTANT_Class,对应结构体为CONSTANT_Class_info,由此知解析的第二个常量的数据段为07 00 0F

CONSTANT_Class_info {
     u1 tag; 
     u2 name_index; 
}

最后得到结果:该常量是一个类的名称,类名存放在constant_pool[15] = "TestClass" , 即constant_pool[2] = TestClass

解析第二个常量

  • 使用javap命令解析class文件

按照上面的方法,可以手动解析出剩下的15个常量。我们也可以通过javap完成对class文件的解析,javap命令使用方法和输入结果如下:

D:\TestClass>javap -verbose TestClass
Classfile /D:/TestClass/TestClass.class
  Last modified 2017-2-7; size 269 bytes
  MD5 checksum b2a3f1078d18eba859aaeac73bd7621d
  Compiled from "TestClass.java"
public class TestClass
  SourceFile: "TestClass.java"
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#14         //  java/lang/Object."<init>":()V
   #2 = Class              #15            //  TestClass
   #3 = Class              #16            //  java/lang/Object
   #4 = Utf8               TEST
   #5 = Utf8               Ljava/lang/String;
   #6 = Utf8               ConstantValue
   #7 = String             #17            //  test string
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               SourceFile
  #13 = Utf8               TestClass.java
  #14 = NameAndType        #8:#9          //  "<init>":()V
  #15 = Utf8               TestClass
  #16 = Utf8               java/lang/Object
  #17 = Utf8               test string

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容