Java字节码扩展
异常表
代码一:
public class Test03 {
public void test() {
try {
InputStream is = new FileInputStream("123.txt");
ServerSocket serverSocket = new ServerSocket(1234);
serverSocket.accept();
} catch (FileNotFoundException f) {
} catch (IOException i) {
} catch (Exception e) {
} finally {
System.out.println("finally");
}
}
}
编译代码一,然后利用javap进行反编译查看结果:
...
Code:
stack=3, locals=4, args_size=1
0: new #2 // class java/io/FileInputStream
3: dup
4: ldc #3 // String 123.txt
6: invokespecial #4 // Method java/io/FileInputStream."<init>":(Ljava/lang/String;)V
9: astore_1
10: new #5 // class java/net/ServerSocket
13: dup
14: sipush 1234
17: invokespecial #6 // Method java/net/ServerSocket."<init>":(I)V
20: astore_2
21: aload_2
22: invokevirtual #7 // Method java/net/ServerSocket.accept:()Ljava/net/Socket;
25: pop
26: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
29: ldc #9 // String finally
31: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
34: goto 84
37: astore_1
38: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
41: ldc #9 // String finally
43: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
46: goto 84
49: astore_1
50: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
53: ldc #9 // String finally
55: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: goto 84
61: astore_1
62: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
65: ldc #9 // String finally
67: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
70: goto 84
73: astore_3
74: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
77: ldc #9 // String finally
79: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
82: aload_3
83: athrow
84: return
Exception table:
from to target type
0 26 37 Class java/io/FileNotFoundException
0 26 49 Class java/io/IOException
0 26 61 Class java/lang/Exception
0 26 73 any
...
因为我们讨论是异常表,所以我只贴处test方法的code attribute的部分内容。
首先看第一行:stack=3, locals=4, args_size=1,stack=3表示test方法运行的任何时刻所能达到的操作数栈的最大深度为3,locals=4表示test方法执行期间创建的局部变量的数目为4,args_size=1表示test方法的参数个数为1。这时我们过来看代码,局部变量表的个数:catch块中,首先InputStream,然后ServerSocket,共有2个局部变量;catch块中会产生一个局部变量;finally中没有局部变量;那么也就只有3个局部变量,而不是4个。而且,test方法没有参数,为什么 args_size的值为1。
这里需要引入一个知识点:this关键字
对于Java中的每一个实例方法(非static方法),其在编译后所生成的字节码中, 方法参数的数量总是比源代码中方法参数的数量多一个(this),它位于方法的第一个参数。这样,我们就可以在Java的实例方法中使用this来去访问当前对象的属性以及其他方法。这个操作是在编译期间完成的,即由javac编译器在编译的时候将对this的访问转换为对一个普通实例方法参数的访问。接下来在运行期间,由JVM调用实例方法时,自动向实例方法传入该this参数。所以,在实例方法的局部变量中,至少会有一个指向当前对象的局部变量。
接下来看异常表:
Exception table:
from to target type
0 26 37 Class java/io/FileNotFoundException
0 26 49 Class java/io/IOException
0 26 61 Class java/lang/Exception
0 26 73 any
异常表中有4个值,每个值的start_pc都为0,end_pc都为26,这是因为catch块catch的都是try块中的内容;handler_pc(处理异常的代码的开始处)分别为37、49、61、73,我们可以发现每段异常的方法都是相同的(因为catch本身就没有写处理方法,直接进入finally块),我们可以发现;catch_type分别是FileNotFoundException、IOException、Exception、any,前三个是catch块catch的异常,any表示处理任何异常。
Java字节码对于异常的处理方式:
- 统一采用异常表的方式来对异常进行处理。
- 在jdk1.4.2之前的版本中,并不是使用异常表的方式来对异常进行处理,而是采用特定的指令。
- 当异常处理存在finally语句块时,现代化的JVM采用的处理方式是将finally语句块的字节码拼接到每一个catch块的后面。换句话说,程序中存在多少个catch块,就会有在每个catch块后面重复多少个finally语句块的字节码。
栈帧与操作数栈
- 栈帧(stack frame):栈帧是一种用于帮助虚拟机执行方法调用及方法执行的数据结构。
- 栈帧本身是一种数据结构,封装了方法的局部变量表、动态链接信息、方法的返回地址以及操作数栈等信息。
符号引用与直接引用
比如现有两个类:A类和B类,A类存在对B类的方法调用,在编译期间,A对B的方法调用,它们之间的地址关系是不知道的。
什么时候才能知道呢?
- 类加载的时候
- 真正开始调用的时候
符号引用:A类去调用B类,在A的常量池中,会维护B的全局唯一限定名
直接引用:直接在内存中寻找到被调用的方法
有些符号引用是在类加载阶段或是第一次使用时就会转换为直接引用,这种转换叫做静态解析
静态解析的四种情形,以下四类方法称作非虚方法,他们是在类加载阶段就可以将符号引用转换为直接引用
静态方法
父类方法
构造方法
私有方法
另外一些符号引用则是在每次运行期转换为直接引用,这种转换叫做动态链接,这体现为Java的多态性
方法的静态分派和动态分派
方法的静态分派
Grandpa g1 = new Father();g1的静态类型是Grandpa,该g1的实际类型(真正指向的类型)是Father
结论:变量的静态类型是不会发生变化,而变量的实际类型则是可以发生变化的(多态的一种体现),实际类型在运行期方可确定。
方法的动态分派:
nvokevirtual字节码指令的动态查找流程
- 寻找操作数栈的栈顶的元素所指向的对象的实际类型
- 如果在实际类型在当中寻找到了与常量池中描叙符和名称都相同的方法,并且具备相应的访问权限,返回目标方法的直接引用
- 如果找不到,就按照继承的层次关系从子类往父类依次重复查找流程;如果一直找不到,则抛出异常
通过比较方法重载和方法重写,得到这样一个结论:
- 方法重载是静态的,是编译期行为
- 方法重写是动态的,是运行期行为
基于栈的指令集于基于寄存器的指令集
现代JVM在执行Java代码的时候,通常会将解释执行和编译执行二者结合起来执行。
- 解释执行:通过解释器来读取字节码,遇到相应的指令就去执行该指令。
- 编译执行:通过即时编译器(JIT:Just In Time)将字节码转换为本地机器码来执行;现代JVM会根据代码热点来生成相应的本地机器码
基于栈的指令集与基于寄存器的指令集之间的关系:
- JVM执行指令时所采取的方式基于栈的指令集
- 基于栈的指令集主要的操作有入栈和出栈两种
- 基于栈的指令集的优势在于它可以在不同平台之间移植;而基于寄存器的指令集是与硬件架构紧密相关的,无法做到可移植
- 基于栈的指令集缺点在于完全相同的操作,指令数量通常要比基于寄存器的指令集数量要多 基于栈的指令集是在内存中完成操作的,而基于寄存器的指令集是直接由CPU来执行的,它是在高速缓冲区中进行执行的,速度要快很多 虽然虚拟机可以采用一些优化手段,但总体来说,基于栈的指令集的执行速度要慢一些
Java字节码扩展的更多相关文章
- Java字节码操纵框架ASM小试
本文主要内容: ASM是什么 JVM指令 Java字节码文件 ASM编程模型 ASM示例 参考资料汇总 JVM详细指令 ASM是什么 ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既 ...
- Java字节码 小结
Reference javap 基本使用方法 深入理解java字节码 从Java代码到字节码 Java字节码.class文件案例分析 字节码 核心概念 Class文件是8位字节流,按字节对齐.之所以称 ...
- JVM 内部原理(六)— Java 字节码基础之一
JVM 内部原理(六)- Java 字节码基础之一 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...
- 轻松看懂Java字节码
java字节码 计算机只认识0和1.这意味着任何语言编写的程序最终都需要经过编译器编译成机器码才能被计算机执行.所以,我们所编写的程序在不同的平台上运行前都要经过重新编译才能被执行. 而Java刚诞生 ...
- @使用javap反编译Java字节码文件
在Sun公司提供的JDK中,就已经内置了Java字节码文件反编译工具javap.exe(位于JDK安装目录的bin文件夹下). 我们可以在dos窗口中使用javap来反汇编指定的Java字节码文件.在 ...
- Java字节码
Java字节码 javap -c 反编译.class文件可得字节码 知乎讨论https://www.zhihu.com/question/27831730 栈和局部变量操作 将常量压入栈的指令 aco ...
- 从Java源码到Java字节码
Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...
- Java字节码指令收集大全
Java字节码指令大全 常量入栈指令 指令码 操作码(助记符) 操作数 描述(栈指操作数栈) 0x01 aconst_null null值入栈. 0x02 iconst_m1 -1(int)值入栈. ...
- Java字节码深度剖析
Java字节码文件查看 我们有一个类Test01,具体内容如下: package bytecode; public class Test01 { private int i = 0; public i ...
随机推荐
- vue 百度地图多标注展示和点击标注进行的提示
index.html中加入script <script type="text/javascript" src="http://api.map.baidu.com/a ...
- CentOS 7上安装Apache
安装apache yum install httpd vi /etc/httpd/conf/httpd.conf 修改默认端口为81 service httpd start netstat -nltp ...
- ex1
#include <stdio.h> int main() { int x; printf("输入一个整数: \n"); scanf("%d",&a ...
- Fix multiple GPUs fails in training Mask_RCNN
Test with: Keras: 2.2.4Python: 3.6.9Tensorflow: 1.12.0 ================== Problem: Using code from h ...
- 编译错误: file not found with angled include use quotes instead #include <lualib.h> 和 #include "lualib.h"
http://stackoverflow.com/questions/17465902/use-of-external-c-headers-in-objective-c 问题: 7down votef ...
- Gradle系列教程之依赖管理
这一章我将介绍Gradle对依赖管理的强大支持,学习依赖分组和定位不同类型仓库.依赖管理看起来很容易,但是当出现依赖解析冲突时就会很棘手,复杂的依赖关系可能导致构建中依赖一个库的多个版本.Gradle ...
- SpringBoot之AOP使用
说到SpringBoot,难免会想到Spring.对于Spring,我曾用其开发过很多大大小小的项目.当使用SpringBoot之后,给人最直观的感受,用古人一句话:”大道至简”. SpringBoo ...
- 012 webpack中的router
一:准备工作 1.App.vue <template> <div> <h1>这是 App 组件</h1> </div> </templ ...
- VS2015 创建C++动态库及使用
转载:https://blog.csdn.net/w_x_myself/article/details/82252646 1.dll的特点 代码复用是提高软件开发效率的重要途径.一般而言,只要某部分代 ...
- thinkphp5---路由问题
在做thinkphp的开发项目中,遇到一个需求:要求让网站的链接,必须以 .html结尾. 原因:在thinkphp开发的项目中,使用伪静态,路由格式:xxx.com/xxx/2.html ,但是后面 ...