首先 , 最大栈深为2 , 在函数执行的任意时刻都不会超过这个操作数栈深度的最大值;然后局部变量数为1 , 表示局部变量表所需的存储空间 , 单位为Slot , 此单位是JVM为局部变量分配内存所使用的最小单位 。蓝圈里还有args_sige表示方法接受的参数数 , 这里为1 , 也就是 this 。最后就是函数代码块里转成的字节码指令里 。
字节码指令不在文章的讨论范围内 , 不妨简单了解 。
字节码指令代表着某种特定操作 , 由一个字节长度代表其操作含义 , 后面可以跟随0到多个所需操作数 。
本例子中的初始化函数被翻译成了:
2A B7 00 01 2A 10 7B B5 00 02 B1
也就是上图蓝圈处的:
{
// 将 this 入栈
0: aload_0
// 唤醒父类实例化函数
1: invokespecial #1
4: aload_0
// 将 123 入栈
5: bitpush 123
// 访问字段 m , 将123存入
7: putfield #2
// 方法返回
10: return
}
这里只说明 putfield #2 , 对应的字节码为 B5 00 02 , 其中 B5 代表操作执行 00 02 为操作所需参数 , 值为 0x0002 , 表示指向常量池类型为 CONSTANT_Fieldref_info 的常量 。CONSTANT_Fieldref_info 结构为
// 伪代码
CONSTANT_Fieldref_info
{
u1 tag;
// 指向常量池类型为CONSTANT_ClassInfo_info的常量
// 代表字段所属类
u2 class_index;
// 指向常量池类型为CONSTANT_NameAndType_info的常量
// 代表字段名和类型
u2 name_and_type_index;
}
CONSTANT_ClassInfo_info
{
u1 tag;
// 指向常量池尾CONSTANT_Utf8_info的常量
// 代表类名
u2 name_index;
}
CONSTANT_NameAndType_info
{
u1 tag;
// 指向常量池尾CONSTANT_Utf8_info的常量
// 代表 名称
u2 name_index;
// 指向常量池尾CONSTANT_Utf8_info的常量
// 代表 所属类型
u2 descriptor_index;
}
当前为指向第二个常量 , 对照信息:
文章插图
m初始化
能知道字节码操作 B5 00 02 是将栈中的 123 给 TestClass.m 进行赋值 , 也就是例子中定义的 private int m = 123 的赋值操作 。
而 x 的赋值则在类构造器<cinit>中进行 , inicrea()和m()函数也可以用相同的方式进行分析 。不再陈述 , 点到为止 。
08 总结
至此 , 了解.Class文件的如何 , 通过.Class文件格式表可以解析出文件内容的基本信息 。.Class文件可以看成多张表的集合 , 根据表制定的规则 , 顺藤摸瓜 , 自然能找出对应信息 。
.Class文件在如何表达信息上不难理解 , 难的是有耐心去缕清这些琐碎的索引关系 , 尤其常量池和属性表部分 。属性表部分则提供了足够的发挥空间 , 根据场景提供更多内容 。
文章仅了解了解析.Class文件的基本规则 , 更进一步的解析规则感兴趣或需要时再了解即可 , 方法不变 。
参考
《深入理解Java虚拟机》 —— 第6章
1、深入理解JVM字节码执行引擎https://blog.csdn.net/suifeng629/article/details/823497842、java中class文件的意义是什么?https://blog.csdn.net/dangbai01_/article/details/800973403、java语言为什么可以跨平台https://blog.csdn.net/banjing_1993/article/details/82349013
欢迎在留言区留下你的观点 , 一起讨论提高 。如果今天的文章让你有新的启发 , 学习能力的提升上有新的认识 , 欢迎转发分享给更多人 。
推荐阅读
- 如何快速在mysql中生成大量Mock数据做性能测试?
- 茶烟自芬芳
- 5 分钟讲明白 JVM、Java 、Java对象模型
- 聊聊在AOP模式下的缓存方案
- 梦见家人反对我的感情 梦见家人反对我和男友在一起
- 茶乡 茶事 茶情
- 新鲜佛手柑的吃法
- 手臂变粗的原因是什么?
- 手冻伤后变粗怎么恢复
- 挡鼠板怎么安装 挡鼠板一般在哪里能买到