JVM(五)手动解析.class文件
一:不同进制之间的转换
二进制:逢2进1,数字0-1。
八进制:逢8进1,数字0-7。三位二进制表示一位八进制。三位二进制最大为111,最大为7。
十进制:逢10进1,数字0-9。四位二进制表示一位十进制数,四位二进制组合有16种数字,取其中10个作为十进制的0-9数字的表示。称为BCD编码。8-4-2-1编码。
十六进制:逢16进1,数字0-9,A,B,C,D,E,F。四位二进制表示一位16进制。最大的二进制1111刚好是F。
二:小端和大端
大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放;这和我们的阅读习惯一致。
小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。

网络传输是大端模式。主机存储是小端模式。
三:Class文件组成
一个class文件是由多部分组成的:
u1占一个字节。u2占两个字节。u4占四个字节。

注意:
1)字节码文件中,如果实现的接口个数=0,下面的实现的接口部分是不存在的。
2)常量池有三种:(1)class中的常量池--静态的 (2)运行时常量池--动态的(3)字符串常量池 StringTable

常量池中的具体的数据项的数据结构如下:
CONSTANT_Class_info {
u1 tag;
u2 name_index; //CONSTANT_Utf8_info
}
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index; //CONSTANT_Class_info
u2 name_and_type_index; //CONSTANT_NameAndType_info
}
CONSTANT_Methodref_info {
u1 tag;
u2 class_index; //CONSTANT_Class_info
u2 name_and_type_index; //CONSTANT_NameAndType_info
}
CONSTANT_InterfaceMethodref_info {
u1 tag;
u2 class_index; //CONSTANT_Class_info
u2 name_and_type_index; //CONSTANT_NameAndType_info
}
CONSTANT_String_info {
u1 tag;
u2 string_index; //CONSTANT_Utf8_info
}
CONSTANT_Integer_info {
u1 tag;
u4 bytes; //直接数
}
CONSTANT_Float_info {
u1 tag;
u4 bytes; //直接数
}
CONSTANT_Long_info {
u1 tag;
u4 high_bytes; //直接数
u4 low_bytes; //直接数
}
CONSTANT_Double_info {
u1 tag;
u4 high_bytes; //直接数
u4 low_bytes; //直接数
}
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index; //CONSTANT_Utf8_info
u2 descriptor_index; //CONSTANT_Utf8_info
}
CONSTANT_Utf8_info {
u1 tag;
u2 length; //直接数
u1 bytes[length]; //改进版的UTF8
}
与标准UTF8不同的地方:
- null字符使用2字节格式,而不是标准的1字节
- 避免内嵌null时分隔问题
- JVM仅使用1/2/3字节的UTF8,不使用标准中4字节格式
- 使用2个3字节UTF8来表示
- 字符串结尾不会追加null终止符
- 紧凑
class文件结构中的成员属性的数据结构

attributes_count是指成员属性的属性个数,比如类属性是final的,就会有。如果这个数为0,也就没有下面的attribute_info了。
class文件结构中的成员方法的数据结构

上面提到的attribute_info所说的数据结构






描述符补充:
void的描述符是:v。我们经常会看到()v,表示返回值为空的一个方法。
LClassName; 表示一个类的实例。例如一个String的实例:Ljava/lang/String;
也可以组合使用。[B表示一个字节数组。
上面是数值的描述符。方法的描述符是: (数据类型的描述符)返回值的描述符。比如main函数的描述符是:([Ljava/lang/String;)V

不同JDK版本有不同的主版本号:

四:解析Class文件
在idea中可以通过Jclasslib ByteCode viewer插件查看字节码里面内容。
我们以下面的java类为例:

安装好插件之后,选中这个类点击View,选择

在右侧就可以看到class里面都有那些内容:

一般信息是class文件信息的汇总。这个信息就是解析.class文件得来的。我们尝试解析.class文件来得到里面的内容。
我们这里使用UltraEdit来打开.class文件,可以看到最原始的十六进制的数据。


我们按照上面class文件的结构来一行一行解析。
1:魔数
这里指用来判断文件类型的魔数,就是上面的CA FE BA BE。如果开头不是这个魔数,这个java的class文件就不合法。
2:次版本号
占两个字节这里是 00 00换算成十进制:0
3:主版本号
占两个字节,这里是 00 34换算成十进制:52。 JDK版本不对的时候经常看到这个数字,这是1.8版本的
4:常量池的大小
占两个字节,这里就是00 29换算成十进制:41。
目前我们按照class的结构解析了四个内容,和插件解析出来的内容是一致的。接下来就是常量池里面的内容了,但是这里有个注意的点常量池是从01开始计数的,常量池里面实际数据项的大小是计算出来的常量池大小-1。
插件解析出来的常量池也是如此。


5:常量池里面数据项的解析
我们就解析前几项数据,其它的解析都是一样的按字节大小往后算。常量池数据项的数据结构按上面列出的十一项常量池数据结构来。
第一项是tag占一个字节。看tag的数值是多少来确定是哪一种数据类型。
1)常量池大小后面的第一个字节是:0A换算成十进制是 10. tag是10 的是数据结构是:CONSTANT_Methodref_info
有两个描述符的索引值index分别占两个字节,分别是:00 07,00 1A,换算成十进制分别是:7,26.因此常量池第一项数据结构就是:
CONSTANT_Methodref_info {
u1 tag; // #10
u2 class_index; //CONSTANT_Class_info #7 指的就是符号引用
u2 name_and_type_index; //CONSTANT_NameAndType_info #26 指的就是符号引用
}
2)第二项数据结构的tag是:08,换算成十进制是:8,对应的数据结构是: CONSTANT_String_info
有一个指向字符串字面量的索引index 占两个字节: 00 1B 对应的十进制:27
第二项数据结构:
CONSTANT_String_info {
u1 tag; // 8
u2 string_index; //CONSTANT_Utf8_info #27 表示是对常量池中第27 个 CONSTANT_Utf8_info的引用
}
3)第三项数据结构的tag: 09 对应的数据结构是:CONSTANT_Fieldref_info
class_index是 00 1C 十进制是 28
name_and_type_index 00 1D 十进制是 29
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index; //CONSTANT_Class_info
u2 name_and_type_index; //CONSTANT_NameAndType_info
}
4)第四项数据结构的tag:08 对应 对应的数据结构是: CONSTANT_String_info
有一个指向字符串字面量的索引index 占两个字节: 00 1E 对应的十进制:30
数据结构项:
CONSTANT_String_info {
u1 tag; // 8
u2 string_index; //CONSTANT_Utf8_info #30 表示是对常量池中第30 个 CONSTANT_Utf8_info的引用
}
常量池中数据我们先解析到这里,下面都是按照这种套路来的,首先根据tag来确定数据结构然后看此数据结构下有几个子属性占几个字节,依次类推。
我们通过计算常量池到如下这行的位置就结束了。

按照class文件的结构下面就是access flags-类的访问控制权限
6:access flags-类的访问控制权限
占两个字节,00 21。这个表示什么呢?从类访问和属性修饰符标识表 中没有找到这个数字,其实这是两个修饰符共同作用的结果。
00 21 是0x0001 | 0x0020的结果,也就是ACC_PUBLIC 和ACC_SUPER共同作用的。
7:类名
占两个字节,00 06指向常量池中的第六项数据。就是我们的全限定类名。

8:父类名
占两个字节,00 07,常量池中的第七项数据如下:

9:实现接口个数
占两个字节,是00 00,我们这个类没有实现接口,那么它下面哪一项实现的接口就在class文件中不存在了。
这里我们也可以得出一个结论Java中一个可以实现的最多的接口数量就是FFFF。
10:成员属性
占两个字节,00 00,这里是没有的。所以下面的值也是没有的。
11:成员方法。
占两个字节是 00 02,有两个成员方法。按照上面说的成员方法的数据结构来解析。
第一个方法:
u2 access_flag: 00 01 ACC_PUBLIC
u2 name_index: 00 08 常量池中第八项数据。<init>方法

u2 descriptor_index : 00 09 常量池中第九项数据。就是个描述符()V,具体意思下面再讲。

u2 attributes_count: 00 01。有一个attribute_info属性。
接下来就是attribute_info的数据结构。
u2 attribute_name_index: 00 0A 常量池的第十项数据。

当前是Code_attribute的数据结构。
u4 attribute_length: 00 00 00 2F 十进制是47.表示接下来的属性一共占用47个字节。
u2 max_stack:操作数栈大小。00 01
u2 max_locals: 局部表大小。00 01
u4 code_length: 00 00 00 05
u1 code(方法体,字节码指令):(连续5个)2A B7 00 01 B1.可以从jclasslib中看到他们代表什么字节码。

aload_0可以点击进去看到。



u2 exception_table_length长度为00 00.下面紧挨着部分就没有内容了。
u2 attributes_cout: 00 02 。 Code属性的属性。
再接下来两个字节是 00 0B.在常量池中是:第11个数据项:

接下来这个是LineNumberTable的数据结构:
u2 attribute_name_index: 就是00 0B。
u4 attribute_length: 00 00 00 06(属性的总长度)
u2 line_number_table_length:有多少个下面的数据结构: 00 01 就一个
u2 start_pc: 00 00
u2 line_number 00 03
从jclasslib中也可以看到:

再接下来就是:局部变量表了。
u2 attribute_name_index : 00 0C 是12

u4 attribute_length:属性的总长度:00 00 00 0C 一共占12个字节
u2 local_varibale_table_length:一共有多少个下面的数据结构:00 01
u2 start_pc: 00 00
u2 length: 00 05
u2 name_index: 00 0D
u2 descriptor_index: 00 0E
u2 index : 00 00
和jclasslib翻译出来的是一致的。

12:除了方法,就剩下属性了

就按照上面列出的数据结构解析。
JVM(五)手动解析.class文件的更多相关文章
- JVM系列文章(三):Class文件内容解析
作为一个程序猿,只知道怎么用是远远不够的.起码,你须要知道为什么能够这么用.即我们所谓底层的东西. 那究竟什么是底层呢?我认为这不能一概而论.以我如今的知识水平而言:对于Web开发人员,TCP/IP. ...
- 手动解析Excel获取文件元数据
工作中有遇到需要获取上传的Excel文件的列明.最大行数.大小等元数据信息.通常做法是通过Apache的POI工具加载文件然后再读取行列进行处理.这种方法很大的弊端就是需要把excel文件加载到内存, ...
- 时序数据库 Apache-IoTDB 源码解析之文件索引块(五)
上一章聊到 TsFile 的文件组成,以及数据块的详细介绍.详情请见: 时序数据库 Apache-IoTDB 源码解析之文件数据块(四) 打一波广告,欢迎大家访问IoTDB 仓库,求一波 Star. ...
- JVM(五):探究类加载过程-上
JVM(五):探究类加载过程-上 本文我们来研究一个Java字节码文件(Class文件)是如何加载入内存中的,在這個过程中涉及类加载过程中的加载,验证,准备,解析(连接),初始化,使用,销毁过程,并探 ...
- 转:VC解析XML文件-CMarkup的使用详解
本篇文章是对VC解析XML文件-CMarkup的使用进行了详细的分析介绍,需要的朋友参考下 VC解析XML文件的工具有很多,CMarkup, tinyXML,还有IBM的,MS的等等. 据说tinyX ...
- 解析PE文件
最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出 ...
- XML:使用DOM技术解析xML文件中的城市,实现select级联选择
中国的城市xml格式:cities.xml <?xml version="1.0" encoding="utf-8"?> <china> ...
- java解析XML文件
dom4j是一个Java的XML API,类似于jdom,用来读写XML文件的.dom4j是一个非常非常优秀的Java XML API,具有性能优异.功能强大和极端易用使用的特点,同时它也是一个开放源 ...
- ACEXML解析XML文件——我是如何学习并在短时间内掌握一个库的使用方法的
最近做的C++项目中需要使用xml文件保存一些信息,程序启动时会读取这些信息.最终经过主程的评测,决定使用ACEXML库来读取解析XML文件. 好吧,至于为什么选择ACEXML库,我就不说了.既然选择 ...
随机推荐
- YZMCMS V5.3后台 SSRF
当改变命运的时刻降临,犹豫就会败北. 前言 此前在测试过程中遇到过此CMS,久攻不下,于是便尝试代码审计,不得不说这套CMS还是挺安全的,读起来也简单,也适合初学代码审计的同学去阅读,不过漏洞真的不多 ...
- 庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署
庐山真面目之九微服务架构 NetCore 基于 Docker 基础镜像和挂载文件部署 一.简介 我们在上一篇文章<庐山真面目之八微服务架构 NetCore 基于 Dockerfile ...
- pixi.js 简单交互事件(点击、缩放、平移)
注意:本文代码使用的Pixi.js版本为PixiJS 5.3.3 pixi中常用的鼠标交互事件: //兼容鼠标和触摸屏的共同触发 type InteractionPointerEvents = &qu ...
- iOS UIcollectionView 实现卡牌翻转效果
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica ...
- C# 托管与非托管类型 堆和栈 值类型与引用类型 装箱与拆箱
一.托管类型与非托管类型 1.托管类型 托管类型包括 引用类型 以及 包含有引用类型或托管类型成员的结构. 引用类型 含引用类型或托管类型成员(字段.自动实现 get 访问器的属性)的结构 // 托管 ...
- 【程序包管理】Linux软件管理之src源码安装编译
在很多时候我们需要自定义软件的特性,这时就需要用到源码安装.那么,网上有很多编译源码的工具,那么,我们怎么知道别人使用的是什么工具呢.其实我也不知道(*^▽^*). 那么本篇博客主要是写C代码的源码安 ...
- Python文件部分(不包括数据)
一,基本操作过程:1.a = open(文件名 ,打开方式) 2.a.read(size) | a.readline(size) | a.readlines(hint) 或 a.write(s) | ...
- Linux中的System V信号量
在进程同步,并发运行时,保证按序地访问共享资源是十分重要的.因此引入了临界区的概念,一次只能有一个线程进入临界区完成他的指令.而信号量(semaphore)的作用,类似于一个交通信号灯,它负责进程协作 ...
- eclipse再见,android studio 新手入门教程(一)基本设置
写在前面: 作为一个刚半只脚踏入android开发的新手,在使用eclipse开发了两个自我感觉不甚成熟的商城类app之后,遇到了一些问题,总结为如下: 代码复用性.findviewById,oncl ...
- char*,const char*和string 互转
1. string转const char* 1 string s = "abc"; 2 const char* c_s = s.c_str(); 2. const char*转st ...