异常表

代码一:

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=1stack=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字节码扩展的更多相关文章

  1. Java字节码操纵框架ASM小试

    本文主要内容: ASM是什么 JVM指令 Java字节码文件 ASM编程模型 ASM示例 参考资料汇总 JVM详细指令 ASM是什么 ASM是一个Java字节码操纵框架,它能被用来动态生成类或者增强既 ...

  2. Java字节码 小结

    Reference javap 基本使用方法 深入理解java字节码 从Java代码到字节码 Java字节码.class文件案例分析 字节码 核心概念 Class文件是8位字节流,按字节对齐.之所以称 ...

  3. JVM 内部原理(六)— Java 字节码基础之一

    JVM 内部原理(六)- Java 字节码基础之一 介绍 版本:Java SE 7 为什么需要了解 Java 字节码? 无论你是一名 Java 开发者.架构师.CxO 还是智能手机的普通用户,Java ...

  4. 轻松看懂Java字节码

    java字节码 计算机只认识0和1.这意味着任何语言编写的程序最终都需要经过编译器编译成机器码才能被计算机执行.所以,我们所编写的程序在不同的平台上运行前都要经过重新编译才能被执行. 而Java刚诞生 ...

  5. @使用javap反编译Java字节码文件

    在Sun公司提供的JDK中,就已经内置了Java字节码文件反编译工具javap.exe(位于JDK安装目录的bin文件夹下). 我们可以在dos窗口中使用javap来反汇编指定的Java字节码文件.在 ...

  6. Java字节码

    Java字节码 javap -c 反编译.class文件可得字节码 知乎讨论https://www.zhihu.com/question/27831730 栈和局部变量操作 将常量压入栈的指令 aco ...

  7. 从Java源码到Java字节码

    Java最主流的源码编译器,javac,基本上不对代码做优化,只会做少量由Java语言规范要求或推荐的优化:也不做任何混淆,包括名字混淆或控制流混淆这些都不做.这使得javac生成的代码能很好的维持与 ...

  8. Java字节码指令收集大全

    Java字节码指令大全 常量入栈指令 指令码 操作码(助记符) 操作数 描述(栈指操作数栈) 0x01 aconst_null null值入栈. 0x02 iconst_m1 -1(int)值入栈. ...

  9. Java字节码深度剖析

    Java字节码文件查看 我们有一个类Test01,具体内容如下: package bytecode; public class Test01 { private int i = 0; public i ...

随机推荐

  1. 【NOI2002】荒岛野人(信息学奥赛一本通 1637)(洛谷 2421)

    题目描述 克里特岛以野人群居而著称.岛上有排列成环行的M个山洞.这些山洞顺时针编号为1,2,…,M.岛上住着N个野人,一开始依次住在山洞C1,C2,…,CN中,以后每年,第i个野人会沿顺时针向前走Pi ...

  2. Watcher监听

    可以设置观察的操作:exists,getChildren,getData 可以触发观察的操作:create,delete,setData   zookeeper观察机制; 服务端只存储事件的信息,客户 ...

  3. putty WinScp 免密登录远程 Linux

    该方法的原理是预先生成一对公钥和私钥,私钥以文件的形式保存在本地,公钥保存在远程机器上.这样每次登录只需指定私钥文件,远程机器通过比对公钥和私钥来验证登录的合法性. Putty 免密登录 第一步 生成 ...

  4. Fluent的summary功能

    在Fluent计算当中,出现错误,大家经常在求助的时候问得很笼统和宽泛,这里介绍一下Fluent的summary功能,大家可以在求助的时候附上生成的文件,这样更加便于别人帮助你发现问题 然后在算例目录 ...

  5. eclipse debug调试 class文件 Source not found.

      1.情景展示 明明有class文件,为什么提示没有? 2.原因分析 这是eclipse与myeclipse的不同之处,myeclipse会自动加载运行时所需的的class文件,而eclipse则需 ...

  6. [NOIP2013]华容道 题解

    [NOIP2013]华容道 首先是一种比较显然的做法. 整个棋盘,除了起点,终点和空格,其他的方块是等价的. 对于终点,它始终不会变化,如果搜到终点结束搜索即可,所以我们不需要考虑终点. 所以需要考虑 ...

  7. Mysql中联合索引的最左匹配原则(百度)

    创建联合索引时列的选择原则 经常用的列优先(最左匹配原则) 离散度高的列优先(离散度高原则) 宽度小的列优先(最少空间原则) 在Mysql建立多列索引(联合索引)有最左前缀的原则,即最左优先.如果我们 ...

  8. Policy Gradient Algorithms

    Policy Gradient Algorithms 2019-10-02 17:37:47 This blog is from: https://lilianweng.github.io/lil-l ...

  9. CentOS 7.5 安装Oracle 11gR2 86%报错:Error in invoking target 'agent nmhs' of makefile

    解决方案: 不要关闭安装过程,另外打开终端窗口,将ins_emagent.mk文件中的 (MK_EMAGENT_NMECTL)更改为$(MK_EMAGENT_NMECTL) -lnnz11,然后在安装 ...

  10. Java学习-057-Jsoup爬虫获取中国所有的三级行政区划数据(二),并生成数据库 SQL 脚本插入语句

    多不废话,直接上马,小主您稳着... package com.fanfengping.zeus.uitl; import com.alibaba.fastjson.JSONObject; import ...