异常表

代码一:

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. AttributeError: module ‘select’ has no attribute 'epoll’

    场景:mac 下导入的 ‘select’ 包 import select,然后在 主函数 中创建的 epoll 对象 epl = select.epoll(),运行报错如下 Traceback (mo ...

  2. Mac上安装Python3虚拟环境(VirtualEnv)教程

    如果已经安装好pip3,那么执行命令安装virtualenv环境 pip3 install virtualenv 安装完成检测版本是否安装成功 virtualenv --version 创建新目录 M ...

  3. Centos7 配置静态IP并使用xshell远程连接

    静态IP配置 1.定位到 /etc/sysconfig/network-scripts文件夹,打开文件夹下面的ifcfg-enp3s0文件 2.修改BOOTPROTO=static  ONBOOT=y ...

  4. fastjson WriteClassName,Double类型不打3.3D

    方式一: public class SerializeConfigX extends SerializeConfig { public SerializeConfigX() { put(Double. ...

  5. 敏捷项目管理—Scrum框架总结

    Scrum中的角色 Scrum Master——项目负责人.项目经理 保护团队不受外界干扰,是团队的领导和推进者,负责提升 Scrum 团队的工作效率,控制 Scrum 中的“检视和适应”周期过程.与 ...

  6. Asp.Net Core Cookie使用,Asp.net Core Cookie操作失效

    注:本文主要介绍Asp.net Core 3.0后增加cookie代理功能. 默认是增加了的. 默认增加的这个有些问题所在, 1.原来设置cookie方式将不可用,需要按照代理方式设置 2.对于ses ...

  7. MyBatis(六):Mybatis Java API编程实现一对多、一对一

    最近工作中用到了mybatis的Java API方式进行开发,顺便也整理下该功能的用法,接下来会针对基本部分进行学习: 1)Java API处理一对多.多对一的用法: 2)增.删.改.查的用法: 3) ...

  8. mybatis自定义之优先从classes目录加载,加载之后遇到相同的类定义时不再加载

    如果mybatis中包含了两个相同定义的mapper,启动时出现下列异常: Caused by: java.lang.IllegalArgumentException: Result Maps col ...

  9. HTML5中的Web Worker技术

    为了让后台程序更好的执行,在HTML5中设计了Web Worker技术.Web Worker的产生主要是考虑到在HTML4中JavaScript Web程序都是以单线程的方式执行的,一旦前面的脚本花费 ...

  10. ASP.NET Core中的Startup

    原文:链接 Startup.cs的作用: 配置各服务和HTTP请求管道. Startup类: ASP.NET Core中使用按惯例Startup命名的类Startup.cs: (可选)包括Config ...