《深入jvm》class结构

记录学习《深入理解Java虚拟机》的第六章,对一个简单程序的class文件进行整个分析

Class类文件的结构

Class文件格式采用一种类似于C语言结构体的伪结构体,这种伪结构体只有两种类型:“无符号数”和“表”

    “无符号数”:当作结构体中的类型

            以u1,u2,u4,u8以代表1,2,4,8字节这些是伪结构体中的基础类型

    “表”:

class伪结构体

    表的结构体中会有cp_info,field_info等的类型,这些也是一个伪结构体他们分别对应的是常量表(cp_info),字段表(field_info),方法表(method_info),属性表(attribute_info)。

我们执行一个程序去分析一个简单程序的class文件


java文件

class文件我是用BZ.exe来分析的

class文件

0xCAFEBABE 0x000000~3 <magic> jvm识别class文件的标准它是固定值

0x0000 0x000004~5 <minor_version> jdk次版本的显示 从jdk1.2后就没有了一直会被设置为0

0x0040 0x000006~7 <major_version> jdk主版本号 十进制为64 为jdk20版本

0x0013 0x000008~9 <constant_pool_count> 常量池的常量数,0x0013转为十进制为19,这表示池中有18个常量,索引值为1~18,0不为索引值,0是作为特殊考虑它表达“不引用如何一个常量池项目”的含义。


17种常量数据类型表(cp_info)

0x00000A~A8 <constant_pool> 常量池,在前面已经知道了有18个常量,在0x00000A位置中的值是[0x0A]对应十进制为10,从常量数据表中(cp_info)找10的tag为CONSTANT_Methodref_info,也就是说在0x00000A~E都是CONSTANT_Methodref_info类型的范围,第一个index值为2指向了当前class文件常量池中的第二个常量,第二个index值为3指向了当前常量池的第三个常量,在地址0x00000F就为当前class文件常量池的第二个常量为CONSTANT_Class_info类型,它的index是指向当前常量池的第四个常量......依次往下推。当到第四个常量时是为CONSTANT_Utf8_info类型在地址0x000017,它的bytes就是一个类名,在class文件中长度为16个字节。注:在class文件中的UTF-8的编码不同,它使用的是UTF-8缩略编码表示的字符

我们可以使用jdk工具里的javap来查看class文件的常量:javap -verbose "class文件"


javap命令输出常量表


    可以看到和前面分析的一毛一样。

0x0000A9~AA <access_flags> 方法标志,用于识别一些类或者接口的层次的访问信息,比如这个Class是类还是接口;是否为public类或者abstract类型;有木有被final修饰等。有个表可以有对照,<access_flags>值为 0x0021 ACC_PUBLIC,ACC_SUPER

访问标准表

0x0000AB~AC <this_class> 类索引用于确定这个类的全限名且是在常量池中寻找 值是8对应常量池中的第8个常量:org/example/TestClass

0x0000AD~AE <super_class> 父类索引引用于确定这个类的父类的全限名,在java中每个类只能有一个父类这个位置不会为空,只有在Object类才会为空 值是2对应第2常量为:java/lang/Object【这里有个疑问当像JRuy或者Jython拥有“多继承”语言时它这里会有什么效果,据我网络查阅说是当JRuy或Jython中的代码涉及到多重继承时,它们会使用Java字节码的接口(interface)机制来模拟多重继承。每个接口都会被编译为一个独立的字节码文件,并且类可以实现多个接口。”具体没有真实的分析过 有兴趣的话可以试一试】

0x0000AF~B0 <interfaces_count> 值为0 接口的数量。由于接口数量为0 <interfaces>不会占用字节。


0x0000B1~B2 <fields_count> “字段”的数量,“字段”的定义是包括类级变量(全局)以及实例级变量(全局性类的实例),但不包括方法内部的局部变量(包括类的实例) 值为1【呃...可以看作存在栈中的不会被看作为“字段”,只有在堆中才会。哈哈哈 栈中变量气抖冷】,“字段”数值为1

字段表


字段访问标志

0x0000B3~BA <fields> 字段 对应着字段信息表和字段访问符0x0000B3~B4为access_flags=0x0002,字段为private【带有<>里面是class伪结构体子元素,不带有的是表里的子元素 如access_flags是表里的子元素】。0x0000B5~B60x0000B7~B8为name_index和descriptor_index他们两的值是常量池的索引name_index=当前常量池的第11个(m 是字段的名字),descriptor_index=当前第12个(I 是字段的类型)

字段类型描述符的参照:B(byte),J(long),C(char),S(short),D(double),Z(boolean),F(float),V(void),I(int),L(对象类型,如Ljava/lang/Object;Ljava/lang/String;Lorg/example/TestClass;),[B(byte[]),[[B(byte[][])以此类推......

0x0000B9~BA attributes_count 【这些属性容易搞混要 注意点,现在这个是字段表里面的attributes_count】 值为0 因为attributes_count为0 attributes也为0

那什么是attributes呢?attributes为属性,在类,字段,方法伪结构体中都会有,在这类,字段,方法中都会有携带自己的属性以描述自己,比如方法里的(code)属性有java编译的代码【在java中代码只会在方法属性中java代码会以字节码的形式在存在方法的code属性,但这可能就会有疑问了如果是代码块呢,代码块不是一个方法而且代码块可出现在方法里外那代码快到代码会放入哪里?在Java的class文件中,代码块不会被作为独立的结构来描述,而是与包含他它们的方法相关联,当如果代码块是在方法里面申请的那好说它字节码会与于当前方法的字节码合并在一起;当代码块是在方法外面那先看当前代码块是否被static修饰,如果是那这个代码块会被称为静态构造代码块它会与静态构造方法的字符串进行合并,如果不是就是构造代码块它会于构造方法合并在一起】

在比如类中可能会有内部类,这个内部类就可以当做一个属性为(innerClasses)内部类列表

属性表结构

每个属性都是按照属性表结构,也就是说每个属性表都会包含上面属性表结构

在《Java虚拟机规范》中可以寻找当前定义的预定义属性,在规范中的4.7解释在jdk20中预定义属性已经有30项了。

方法表
方法标准访问

0x0000BB~BC <methods_count> 值为2,说明在中这个class文件中有两个方法【一个我们创建的inc和<init>方法】

0x0000BD~114 <methods>这是第一个方法(上面是有两个方法嘛) 方法和字段差不多,表结构是一样的 对着上面两个表 。

        0x0000BD~BE access_flags 值为1代表次方法为当前方法被public。0x0000BF~C0 name_index值为5,指向常量池的第5个为“<init>”。

        0x0000C1~C2 descriptor_index值为6,第六个常量池为()V,方法的返回类型和字段差不多的()V翻译过来就是无参无返回,加上一些例子就可以很好理解了:void xxx() 可以描述为name_index=xxx这是方法的名字,descriptor_index=()V为方法的返回类型。对于其他的【结合字段里的对基础类型来看】,简单一点的 int xxx(char c1,int[] is1),名字和上面的一样,descriptor_index=(C[I)I ;复杂一点的 String[] xxx(int i1,int[] is1,String[][] s2),名字和上面一样,descriptor_index=(I[I[[Ljava/lang/String)[Ljava/lang/String

和上面显示的合起来:public void <init>() 

        0x0000C3~C4 attributes_count 值为1说明在当前方法里一个属性,后进入了属性表,0x0000C5~C6 attribute_name_inden这是属性表名称索引 为12,在常量池第十二个常量为“code”,也就是说这是一个code属性表

code属性表


在code属性表中attribute_name_index已经看过了,接下来0x0000C7~CA attribute_length 这个是整个code属性表的长度,由于属性名称于属性表长度,这个长度它是不包含attribute_name_index和attribute_length的少了六个字节。0x0000CB~CC max_stack代表了当前方法最大栈深度,也就是说在执行方法的时候操作数栈不会超过这个值为1。0x0000CD~CE max_locals 这代表了局部变量表所需的空间(就是存局部变量的空间)在这个空间中它是以糟(slot)为最小单位,在从多基础类型中超32位的类型为2糟比如long和double,其他都是1糟在地址中值为1说明当前方法的局部变量表为1糟    [注意1糟不能说明在此方法中只有一个局部变量,在Javac编译器会根据变量的作用域来分配变量糟给各个变量使用,根据同时生存的最大局部变量数量和类型计算出max_locals(这个始终都会有1糟因为即使你在方法中没有定义局部变量也会有一个“this”,"this"访问当前对象,它是一个局部变量)【1糟也说明了当前方法里没有对象和long,double类型的局部变量】]    。0x0000CF~D2 code_length 表示了当前方法里代码的字符码的数量,方法里代码转换的字节码在jvm看来是指令是以一个字节为单位的指令【我感觉像操作系统中的中断表】值为5 说明有5个指令,这些指令在《java虚拟机规范》现在有约200个指令(以一个字节为单位的,8bit,最多只有256个指令)。0x0000D3~D7 code 这个就是存储指令咯,本章最重要的 值为“2A B7 00 01 B1”得去对照这200条指令看虚拟机到底执行了什么。0x0000D8~D9 exception_table_length 这是当前方法里的异常数,当前异常数为0,exception_table就没有了【在简单程序中没有设置异常】。0x0000DA~DB attributes_count 属性数,值为1在当前code表中有一条属性。0x0000DC~DE 值为14,查看常量池,噢?LineNumberTable属性,这个属性是查看方法代码的行号与字节码的对应关系的且这个属性只有code表会有(这个LineNumberTable属性不是运行时必须的可以在运行java时设置-g:none or -g:lines来取消这个)

LineNumberTable表


0x0000DC~0x0000E7是LineNumberTable属性的范围。

0x0000E8~0x000114为描述第二个方法区域,和第一个没什么差别主要就是max_stack比第一个方法多了一个应该是应用了一个常量“1”,然后它的代码方法转换的字节码为7个比第一个多了两个(关于字节码该看还是得看的去对照那200个指令)。

最后到类伪结构体的属性了

0x000115~0x000116 <attributes_count> 值为1,说明类有一个属性。

0x000117~0x000118 值为17 从常量池中寻找第17个看到是SourceFile属性,它是记录源文件名称的从它为结构体看

SourceFile表


0x000117~0x00011E 是它的范围,直接看最后面两个字节值是18,从常量池中寻找。噢~ TestClass.java 你好类名。

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

推荐阅读更多精彩内容