class 文件结构深入解析
什么是 class 文件
能够被 JVM 识别,加载并执行的文件格式,他就类似于 mp3 的格式,只能运用于特定的地方,如 mp3 只能被播放,而 class 文件则是需要被 JVM 进行加载。
并不是只有 java 语言才能生成 class 文件,当然还有其他的一下语言:
如何生成一个 class文件
通过 ide 自动生成 class 文件,通过 run 来执行 calss 文件
通过 javac 生成 class 文件 ,通过 java 命令去执行 class 文件
class 文件的作用
记录一个类文件中的所有信息,记住是所有的信息,包括类名称,方法,变量等,class 中记录的信息远远多于java源代码中的信息。例如:我们并没有在类中定义 this、super 这样的关键字,但是我们可以使用,这是因为 java 虚拟机在生成 class 文件时已经帮我们记录在里面了。
class 文件结构
一种8位字节的二进制文件
各个数据按顺序紧密的排列,没有间隙,不想有些文件,为了读取方便,则不会让数据紧密排列。而 class 文件这样的好处就是 体积小。
每个类或者接口都单独占据一个 class 文件
class 的文件格式采用的是类似结构体的结构来存储数据,这种结构只有两种数据类型:无符号竖和表,其中无符号数属于基本的数据类型,u1,u2,u4,u8 来分别代表 一个字节 ,2,4,8个字节。无符号数可以用阿里描述数字,索引引用,数量值或者 utf-8构成的字符串值,而表是由多个无符号数或其他表构成的复合数据结构,所有的表都以 _info 结尾,表用于描述有层次关系的复合结构数据类型,其实整个 class 文件就是一张表。(但是我觉得更像结构体)
下面看一下具体的结构
类型 |
名称 |
数量 |
作用 |
---|---|---|---|
u4(无符号四字节) |
magic |
1 |
加密段,当前calss 文件是否被串改过 |
u2 |
minor_version |
1 |
最小可以被那个版本的 jdk所加载 |
u2 |
major_version |
1 |
当前calss是别那个版本生成的 |
u2 |
constant_pool_count |
1 |
常量池的数量,通常只有一个 |
cp_info |
constant_pool |
constant_poll_count – 1 |
真正的常量池,它内部包含了很多内容,下面有详细解释 |
u2 |
access_flags |
1 |
作用域标志,例如这个 class 文件是 public 还是 public final 类型的 等等。下面会放一张图 |
u2 |
this_class |
1 |
this,jvm 在生成 class 的时候帮我们补充了这个字段,这就是为什么我们没有定义它却能够使用他了 |
u2 |
super_class |
1 |
和上面的 this 一样 |
u2 |
interfaces_count |
1 |
数量 |
u2 |
interfaces |
interfaces_count |
这两个表示当前类实现了多少个接口,注意:只算直接的,间接的不算,如不会计算父类实现的接口。 |
u2 |
fields_count |
1 |
数量 |
field_info |
fields |
fields_count |
当前class中的所有成员变量,他里面还包含了其他的信息,如 类型,所属的类等 |
u2 |
medthods_count |
1 |
数量 |
medthod_info |
methods |
methods_sount |
记录了class 中所有的方法,包含了其他信息。 |
u2 |
attribute_count |
1 |
数量 |
attribute_info |
attributes |
attributes_count |
记录类属性相关的,上面没有包含的都会在这里面,如注解等。 |
我们看一下这个表格,class 文件中定义了很多字段,这些字段又包含了非常多的内容。通过这些字段,jvm 就可以找到我们类中所有的内容
access_flags :作用域
这个图清楚地表示了 access_flags 字段的作用域。
constant_pool:常量池,下面是几种常用的类型
CONSTANT_Integer_info
CONSTANT_Long_info
CONSTANT_String_info
// 下面这几个里面存储的并不是真的内容,而是索引,这些索引最终指向的就是上面这几种类型,所以我们所有的信息都是存在常量池中的。
CONSTANT_Class_info //类信息,例如名字等,引用到类的一些信息等
CONSTANT_Fieldref_info //类中变量信息
CONSTANT_Methodref_info //类中方法信息
我们可以通过工具来查看一下 class 文件内容,工具名字为 010 Editor。
struct cp_info_constant_pool[0] 中的 u2,代表的是无符号数,u2 代表访问标志,如 u2 class_index 指向的就是这个方法所属的类。相当于索引吧,
注意看 u1 tag:
在常量池中有14种类型,这个常量都是一个表,每一个表都有各自组成的机构,这写常量都有一个特点,每个常量的开始都是一个用 u1 类型的无符号数表示的标志位,如下表所示:
常量池中第一个 struct cp_info constant_poll[0] ,他的后面是 Methodref ,标志是 10 ,我们和上面的表对比一下,找到第 10 个,也是 Methodref ,通过这个表,我们可以查看 u1 tag 到底表示的是那种类型。
如上面的 fields_count 为1,下面就有一个 struct field_info 的表,里面保存了字段的信息,methods_count 为 2,下面就有 methods[0] 和 methods[1]表,来表示这两个方法。这也就对应了上面的表
class 文件弊端
内存占用大,不适合移动端
堆栈的加载模式,加载速度慢
文件 IO 操作多,类查找慢 。每次加载类的时候都要去寻找和加载
dex文件结构深入解析
什么是 dex 文件
能够被 DVM 所识别,加载并执行的文件格式,dex 文件可以 用 c 和c++ 进行生成
如何生成一个 dex 文件
通过 IDE 自动帮我们 build 生成
手动通过 dx 命令生成 dex 文件
dex 文件的作用
记录整个工程中所有类文件的信息,是整个工程(class 则是记录当前类的信息)
dex 文件结构
一种 8 位字节的二进制流文件
各个数据按顺序紧密的排列,无间隙
整个应用中所有的 java 源文件都放在一个dex中,这里不考虑 multidex
dex 文件头
- magic :一般称为魔数,他可以判断当前的 dex 文件是否有效,可以看到他的 大小就是 9 h,也就是他用了 8个1字节的无符号数表示。
- checksum : dex 文件的校验和,通过它可以判断 dex 文件是否被损坏
- signature[] : 用于校验 dex 文件,其实就是把整个 dex 文件用 SHA-1 签名得到的一个值,
- file_size : 表示真个文件的大小,占用了4个字节
- header_size : 表示 dex 头部分的大小,占用4字节
- endian_tga :字节序标记,用于指定 dex 运行环境的 cpu,预设值为0x123456789
- link_size 和 link_off :分别指定了链接段的大小和文件的偏移,通常都为0
- map_off :指定了 DexMapList 的文件偏移
- string_ids_size 和 string_ids_offf:这两个字段表示了 dex 文件中所有用到的字符串的个数和位置偏移。通过转换 size 为16,偏移为112。
下面我们可以看一下 string_ids_size 的16进制和off 的16进制,分别为10 00 00 00 和 70 00 00 00,前者转换10进制后为 16,说明 size 有 16 个字符串,off 则代表偏移 70h , 最后一个空字符“0”表示的是结尾 。然后我们找到70h 看一下
下面我们看一下70h
从70 开始,到 AOh 结束,所选中的都是 字符串的偏移量,通过这些偏移量我们才能找字符串,看一下上面第二个框,是 字符串的索引,可以看到他也是从70h 开始,大小是40h,也就是到A0h。在这段区域内保存的才是真正的字符串索引。我们可以看一下70h 第一个 92 01 00 00 ,他代表的偏移地址就是 0192h,接着我们找一下 0192h
可以看到我一共选中了 8个字节,在这8个字节中我们可以用到的有6个,最开始的 06 则表示我们用到的个数,最后的 0 表示的是字符串结尾,下面我们把他们进行转换一下:
十六进制 |
3C |
69 |
6E |
69 |
74 |
3E |
---|---|---|---|---|---|---|
十进制 |
60 |
105 |
110 |
105 |
116 |
62 |
ASCII |
< |
i |
n |
i |
t |
> |
通过上面这种方式我们就可以找到字符串的16进制,并转换为对应的字符串。
其他的都一样,我们也没有必要像上面遮这样找,其实我们可以直接在索引区找到对应的值。例如上面这个例子:
这里的数据是从 163开始的,是因为他并没有及时前面的 06 ,这个06至少代表后面要用到 6 个而已。
从 9 往下基本都是这个样子的。通过上面这种方法就可以查到具体的位置。
索引区
类型索引:
如 Hellow 类索引,Object 索引,String 索引的。这些都是我们所引用到的。
方法原型索引 :
字段索引:
方法索引:
如上面的 main 方法 ,printStream 打印方法等。。他会记录当前类所引用的方法索引和继承的方法索引
类索引:
整个dex 文件中所有类索引
map 列表:
他是对整个头文件的一个校验
数据区
每个索引对应的值就是数据了。
最后看一下整个 dex 未必会的格式
Class 和 Dex 的区别
每个 class 文件是一个表。这个文件只记录了当前java的信息。
dex 将文件划分为了 三个区域,这三个区域存储了整个工程中所有的java 文件的信息,所以 dex 在类越来越多的时候优势就提现出来了。他只要一个dex文件,很多区域都是可以进行复用的,减少了dex 文件的大小。
本质上他们是一样的,dex 是从 class 文件演变而来的,但是 calss 中存在了许多沉余信息,dex 去掉了沉余信息,并进行了整合
如果本文有帮助到你的地方,不胜荣幸,如有文章中有错误和疑问,欢迎大家提出!
文章来源于互联网:Class 和 Dex 文件深入理解 | JAVA_Android
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/21766,转载请注明出处。
评论0