Java字节码方法表与属性表详解

继续跟着上一次【https://www.cnblogs.com/webor2006/p/9502507.html】的那10个代表code的字节分析,如下:

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

而这些字节其实对应的信息是它:

Java字节码方法表与属性表详解

所以问题就来了,aload_0不是助记符信息么,怎么就能够跟字节码文件中的字节对应上呢?所谓助记符其实也就是帮忙我们去记忆的符合,在底层其实也是对应的一个个十六进制的数字的,其它aload_0对应的就是2A这个十六进制数字,凭什么这么说?因为有jclasslib这么好的工具能帮我们对应上,将鼠标放在助记符上发现是一个可以点的链接,如下:

Java字节码方法表与属性表详解

点一下“aload_0”发现居然链到了oracle的官网上的说明上去了,如下:

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

所以第一个字节已经分析完了,确实是跟助记符对应上了,接着来分析第二个字节:

Java字节码方法表与属性表详解

而在jsclasslib中对应的第二个助记符是“invokespecial”,点击链到官网看一下:

Java字节码方法表与属性表详解

而它的作用可以理解成就是调用父类的方法,很明显对于咱们自定义的子类肯定会去调用父类的构造方法,而这个助记符是有参数的:

Java字节码方法表与属性表详解

其实也就是往后的两个字节就是该助记符所对应的参数,如下:

Java字节码方法表与属性表详解

对应常量池为:

Java字节码方法表与属性表详解

也就是构造方法嘛,如jsclasslib所示:

Java字节码方法表与属性表详解

接下来继续往下走一个字节:

Java字节码方法表与属性表详解

又对应aload_0,如下:

Java字节码方法表与属性表详解

接着再往后看一个字节:

Java字节码方法表与属性表详解

其实它就是对应下一个助记符,如下:

Java字节码方法表与属性表详解

为啥如此任性呢,因为点击查看一下说明就晓了啦:

Java字节码方法表与属性表详解

为啥要push一个1呢?实际就是给咱们定义的成员变量a赋值,如下:

Java字节码方法表与属性表详解

可见,该变量的赋值是在默认构造函数中进行的,而不是直接进行赋值的,这也就是分析字节码文件的好处,可以更加真实的发现底层细节。

接下来再数一个字节:

Java字节码方法表与属性表详解

当然它又对应另一个助记符喽,如下:

Java字节码方法表与属性表详解

点击看一下官网说明:

Java字节码方法表与属性表详解

接下来该助记符是携带有参数的,所以再往后数两个字节:

Java字节码方法表与属性表详解

对应常量池:

Java字节码方法表与属性表详解

也就是给MyTest1的成员变量a赋值为1。

接下来再数一个字节:

Java字节码方法表与属性表详解

对应于如下助记符:

Java字节码方法表与属性表详解

点击确认一下:

Java字节码方法表与属性表详解

至此整个构造方法就已经执行完了。发现通过分析字节码也能获得不少新知识嘛,仅通过这个构造函数的执行流程就能知道了对于我们定义的成员变量原来是在构造方法中进行赋值而非直接赋值的,还是挺有价值的。

好,方法的code分析完之后,则就得往下进行分析了,先来查看一下结构类型:

Java字节码方法表与属性表详解

也就是再数两个字节,看一下:

Java字节码方法表与属性表详解

说明该方法木有异常信息,所以接下来的异常表就不会显示在字节文件当中了:

Java字节码方法表与属性表详解

其中关于异常还需解释一下:

  • exception_table,这里存放的是处理异常的信息。
  • 每个exception_table表项由start_pc、end_pc、handler_pc、catch_type组成。
  • start_pc和end_pc表示在code数组中的从start_pc到end_pc处(包含start_pc,不包含end_pc)的指令抛出的异常会由这个表项来处理。
  • handler_pc表示处理异常的代码的开始处。catch_type表示会处理的异常类型,它指向常量池里的一个异常类。当catch_type为0时,表示处理所有的异常。

接下来就到属性相关的东东了,如下:

Java字节码方法表与属性表详解

所以往下数2个字节:

Java字节码方法表与属性表详解

说明该方法有两个属性,往下数两个字节则是第一个属性的名字索引,如下:

Java字节码方法表与属性表详解

也就是对应第10的常量池,为:

Java字节码方法表与属性表详解

该属性用来表示code数组中的字节码和Java代码行数之间的关系。这个属性可以用来在调试的时候定位代码执行的行数。而该属性的结构为:

Java字节码方法表与属性表详解

其中attribute_name_index就是常量索引10,接下来数4个字节则是属性的长度attribute_length,如下:

Java字节码方法表与属性表详解

也就是属性的长度为10,也就是接下来10个字节则为LineNumberTable的属性信息,如下:

Java字节码方法表与属性表详解

看一下jclasslib:

Java字节码方法表与属性表详解

下面具体来分析一下这10个字节,根据结构体来看:

Java字节码方法表与属性表详解

先2个字节表示属性表有几对映射,如下:

Java字节码方法表与属性表详解

说明有两对映射,然后再回到结构体中,每对映射的内容为:

Java字节码方法表与属性表详解

每对占4个字节,先看第一对映射:

Java字节码方法表与属性表详解

也就是start_pc=0;line_number=3,对应于jclasslib:

Java字节码方法表与属性表详解

由于咱们源代码木有构造方法,所以字节码对应源代码就在第3行,如下:

Java字节码方法表与属性表详解

接下来看第二对映射:

Java字节码方法表与属性表详解

也就是start_pc=4;line_number=4,对应于jclasslib:

Java字节码方法表与属性表详解

因为成员变量的赋值是在构造方法中完成的,所以对应第4行代码:

Java字节码方法表与属性表详解

好,方法的第一个属性已经完了,接下来以同样的顺序来查看方法的第二个属性信息了,走2个字节来看属性名称索引,如下:

Java字节码方法表与属性表详解

对应第11个常量池索引,如下:

Java字节码方法表与属性表详解

它的结构跟LineNumberTable差不多的,往后数四个字节则是局部变量表所占的长度:

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

长度为12,如jclasslib所示:

Java字节码方法表与属性表详解

然后往后数12个字节则是局部变量的具体信息,首先两个字节则为局部变量的个数,如下:

Java字节码方法表与属性表详解

呃~~构造方法哪来的局部变量呢,好奇怪,先不管先来把其它字节分析完,再往后四个字节表示start_pc和length,如下:

Java字节码方法表与属性表详解

如jclasslib所示:

Java字节码方法表与属性表详解

接下来则为局部变量的索引为0,也就是第一个局部变量:

Java字节码方法表与属性表详解

再往后两个字节则是局部变量对应常量池的索引,如下:

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

再接下来两个字节则是对该局部变量的一个描述常量索引,如下:

Java字节码方法表与属性表详解

Java字节码方法表与属性表详解

所以对应jclasslib中可以看到:

Java字节码方法表与属性表详解

那思考一下为啥在构造方法中会有一个this的局部变量呢?我们知道在所有方法中我们都能使用this关键字来访问当前的对象,而从字节层面来讲其实this是作为方法的第一个参数传进来的,也就是说对于Java的一个实例方法而言最少会有一个this的局部变量存在!

还剩最后两个字节则为stackmaptable信息,JDK1.6加入的,主要做校验检查的,因为0嘛所以后面肯定木有相关的信息了,这里就直接忽略,如下:

Java字节码方法表与属性表详解

上一篇:Windows Server 2016-命令行方式管理Windows服务


下一篇:java加密算法入门(三)-非对称加密详解