JVM类文件的结构,16进制分析

本文详细介绍了如何通过Notepad++和HexEditor查看Java .class文件的16进制结构,并解析了Class文件的魔数、版本、常量池、访问标志、继承关系等关键部分。通过javap命令展示了如何使用命令行工具获取类文件的元数据。
摘要由CSDN通过智能技术生成

一、如何查看一个类文件的16进制结构

  1. 写一个简单的java程序,javac编译产生.class文件
  2. 用notepad++打开,一开始可能是乱码引入插件HexEditor
    HexEditor.dllx64版本下载

    HexEditor.dll文件放在Notepad++的plugins文件夹下面,重新打开文件
    在这里插入图片描述
    旁边会多出一个H字样点击之后就可以查看.class文件的16进制的格式了
    在这里插入图片描述
    程序代码:
		public class Test{
			private int m;
			public int inc(){
				return m+1;
			}
		}

二、文件结构

  Class文件格式
  其中u1,u2,u3表示1个字节,2个字节,3个字节
在这里插入图片描述
  而类型带有“-info”的是有层次关系的复合结构数据。有点类似于结构体。

1.魔数(Magic Number)和版本(Minor Version、Major Version)

在这里插入图片描述
魔数:用于确定这个文件是否是一个被虚拟机接受的class文件。
  很多文件存储标准都有魔数,使用魔数辨别文件类别比后缀名更加安全,后缀名可以轻易的改变,而魔数不能例如下面的JPG文件:
在这里插入图片描述
  然后将其后缀名改为GIF:
在这里插入图片描述
  魔数不会改变。这个值可以由文件制定者指定,没被广泛使用且不会引起混淆即可。
  java的魔数cafebabe,咖啡宝贝hhh
版本号:将0x0034转为10进制就是52,下表中只记录了JDK1.7,而我是JDK1.8
在这里插入图片描述
  JDK是向下兼容,即1.7的可以用1.6编译产生的Class文件,而不能用1.8编译产生的。

2.常量池(constant_pool)

  常量池长度不固定,所以入口放置一项u2类型来指明有多少项常量
在这里插入图片描述
  0x0013->19。表明有18项常量,为什么不是19项呢?

为了满足后面某些指向常量池的索引值的数据在特定的情况下表达“不应用任何一个常量池项目”

  然后第一项常量0a->10记为#1,这里又要查表了:
在这里插入图片描述
  10是一个符号引用,然后在查表:
在这里插入图片描述
在这里插入图片描述
  后面两个都是索引向,指向#4#15,而4和15 我们还没有读到,就继续往下读
  第二个0x09->9,项目类型表可知为字段引用符号。然后一直这样读下去,当有01类型时,就将数字对应的ASCII码记录下来,这就是我们能够读懂的地方了。
  是不是很麻烦?而且很机械?既然知道怎么读了,当然是有程序可以帮你翻译的,但是还是要先搞清楚原理嘛

在cmd命令行输入
javap -verbose Test//Test为开始编译的文件名

 
 
  • 1
  • 2

  获得以下结果:
在这里插入图片描述
  常量池部分:
在这里插入图片描述

3.访问标志(access_flags)

  用于识别一些类的或者接口层次的访问信息,包括是类还是接口,是不是public等等
在这里插入图片描述
  如果是则将标志位做 | 运算,如果不是则不管,该类就应该是0x0001|0x0020 = 0x0021:
在这里插入图片描述

4.类索引(this_class),父类索引(super_class)与接口(interfaces)集合

  这三项数据确定了这个类的继承关系。
  除了java.lang.Object外所有java类的父类都不为0,因为object是所有类的粑粑。
在这里插入图片描述在这里插入图片描述
  接口索引集合大小为0,没有接口。

5.字段表集合(filed_info)

  用于描述接口或者类中声明的变量
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  紧跟着接口集合后面的位容量计数器在这里插入图片描述

  本测试文件中的值位0x0001,即只有一个字段表数据
  标志位0x0002–>ACC_PRIVATE为真,简单名称0x0005指向#5,字段和方法描述符0x0006指向#6

标识符含义标识符含义
B基本类型byteJ基本类型long
C基本类型charS基本类型short
D基本类型doubleZ基本类型boolean
F基本类型floatV特殊类型void
I基本类型intL对象类型。如Object

在这里插入图片描述
  在看我们写的程序是:

```java private int m; ```

  是不是就能理解了呢

6.方法表集合

  其方法和字段表一样。
在这里插入图片描述
  唯一不同的就是访问标志和属性表集合的可选项。图中我们可以解读出方法的信息,但是却没有执行的代码。这是因为经过编译器编译成字节码指令后存储在方法的属性集合中一个名为“Code”属性里面,也就是0x0009这个地方在这里插入图片描述

7.属性表(attribute_info)集合

  现在预定义的属性已经增加到了21个
在这里插入图片描述
  属性表的结构

类型名称数量
u2attribute_name_index1
u4attribute_length1
u1infoattribute_length
  • Code属性
      前面说过编译器编译成字节码指令后存储在方法的属性集合中一个名为“Code”属性里面。但接口或者抽象类就不存在Code属性
    在这里插入图片描述
    attribute_name_index是一个指向指向utf8型常量的索引,常量值固定为“Code”。
    由于属性名和属性长度一共为6字节,所以属性值的长度位整个属性表的长度减去6
    max_stack代表操作数栈深度的最大值。虚拟机运行时根据这个值来分配栈帧。
    max_locals代表局部变量表所需的存储空间。单位是Slot

Slot是虚拟机为局部变量分配内存所要使用的最小单位。对于byte、char、float、int、short、boolean 和returnAddress等长度不超过32的占用1个Slot,double和long占用2个,且局部变量表中的Slot可以重用

code_length代表字节码的长度。虽然他是一个u4类型,但实际长度只用了u2。如果超过了javac会拒绝编译。但有些时候复杂的JSP文件用某些JSP编译器会将JSP页面的内容和输出信息归到一个方法之中,从而导致编译失败
在这里插入图片描述
看看代码

		public class Test{
			private int m;
			public int inc(){
				return m+1;
			}
		}

初始化方法init()inc()都没有参数但是Args_size()都为1。这是因为我们常用的this就是这个参数。实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留出第一个Slot位来存放对象实例的引用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值