Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 358|回复: 0

《深入理解Java虚拟机》- JVM如何进行异常处理

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-7-17 01:06:14 | 显示全部楼层 |阅读模式

    一、Java异常

    在程序中,错误可能产生于程序员没有预料到的各种情况,或者超出程序员可控范围的环境,例如用户的坏数据、试图打开一个不存在的文件等。为了能够及时有效地处理程序中的运行错误,Java 专门引入了异常类。

    二、Java常见异常分类

    三、为什么产生异常

    在 Java 中一个异常的产生,主要有如下三种原因:

    1. Java 内部错误发生异常,Java 虚拟机产生的异常。
    2. 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,一般需要在某些类中集中处理这些异常。
    3. 通过 throw 语句手动生成的异常,这种异常称为检査的异常,一般用来告知该方法的调用者一些必要的信息。

    四、碰到异常怎么办?

    我们把生成异常对象,并把它提交给运行时系统的过程称为拋出(throw)异常。运行时系统在方法的调用栈中查找,直到找到能够处理该类型异常的对象,这一个过程称为捕获(catch)异常。

    Java 异常强制用户考虑程序的强健性和安全性。异常处理不应用来控制程序的正常流程,其主要作用是捕获程序在运行时发生的异常并进行相应处理。编写代码处理某个方法可能出现的异常,可遵循如下三个原则:

    1. 在当前方法声明中使用 try catch 语句捕获异常。
    2. 一个方法被覆盖时,覆盖它的方法必须拋出相同的异常或异常的子类。
    3. 如果父类抛出多个异常,则覆盖方法必须拋出那些异常的一个子集,而不能拋出新异常。

    (引用:http://c.biancheng.net/view/1038.html)

    五、从JVM角度看异常的产生与表达

     先看示例代码:

    public class Foo {
      private int tryBlock;
      private int catchBlock;
      private int finallyBlock;
      private int methodExit;
    
    
      public void test() {
        try {
          tryBlock = 0;
        } catch (Exception e) {
          catchBlock = 1;
        } finally {
          finallyBlock = 2;
        }
        methodExit = 3;
      }
    }

    这段代码是一段简单的异常处理代码,我们可以通过javap查看class文件的表达形式:

    public void test();
        descriptor: ()V
        flags: ACC_PUBLIC
        Code:
          stack=2, locals=3, args_size=1
             0: aload_0
             1: iconst_0
             2: putfield      #2                  // Field tryBlock:I
             5: aload_0
             6: iconst_2
             7: putfield      #3                  // Field finallyBlock:I
            10: goto          35
            13: astore_1
            14: aload_0
            15: iconst_1
            16: putfield      #5                  // Field catchBlock:I
            19: aload_0
            20: iconst_2
            21: putfield      #3                  // Field finallyBlock:I
            24: goto          35
            27: astore_2
            28: aload_0
            29: iconst_2
            30: putfield      #3                  // Field finallyBlock:I
            33: aload_2
            34: athrow
            35: aload_0
            36: iconst_3
            37: putfield      #6                  // Field methodExit:I
            40: return Exception table: from to target type 0     5    13   Class java/lang/Exception 0     5    27 any 13    19    27 any
          LineNumberTable:
            line 10: 0
            line 14: 5
            line 15: 10
            line 11: 13
            line 12: 14
            line 14: 19
            line 15: 24
            line 14: 27
            line 16: 35
            line 17: 40
          StackMapTable: number_of_entries = 3
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Exception ]
            frame_type = 77 /* same_locals_1_stack_item */
              stack = [ class java/lang/Throwable ]
            frame_type = 7 /* same */

    从字节码中的注释可以看到,finally块被添加到了三个地方。也就是说,在从java代码翻译成字节码文件时,jvm会为try块和catch块生成finally 块里的逻辑。但是想想,为什么是三个“finally”呢? 最后一个finally 是为在catch块中的代码执行时发生异常而准备的。那么,有人会问,finally块的代码如果还有报错怎么办呢? 这里,引进没有被本人证实的事实:会往外抛出去,给上一层代码进行处理。

    这里说明一下黄色部分的字节码:

    exception table 表示异常表,异常表是用于存储代码中涉及到的所有异常,每个类编译后,都会跟随一个异常表,如果发生异常,首先在异常表中查找对应的行(即代码中相应的 try{}catch(){}代码块),如果找到,则跳转到异常处理代码执行,如果没有找到,则返回(执行 finally 之后),并 copy 异常的应用给父调用者,接着查询父调用的异常表,以此类推。

    from...to:表示异常处理器监控的范围(比如try块包含的代码)

    target:表示异常处理器起始的位置(比如catch块包含的代码)

    type:就是处理的异常

    那么,发生异常后,如何对照异常表?

    当程序触发异常后,Java虚拟机会从上到下遍历异常表中的条目。当触发异常的字节码的索引值在某个异常表条目的监控范围内,Java虚拟机会判断所抛出的异常和该条目想要捕获的异常是否匹配。如果匹配,Java虚拟机会将控制流转移到该条目的target指针指向的代码上,继续程序运行。

    下面,提及的字节码解析一下异常表:

    程序开始,运行到1:iconst_0时,发生Exception异常,此时程序会去便利方法表,从第一行开始,检测到 0<1<5,符合第一条目检测范围,接着再查看抛出的异常为Exception,符合该条目捕获处理的异常,后跳转至序号13字节码继续运行。若再在14:aload_0发生异常时,程序就又跳到异常表,查找匹配异常条目,最终找到target为序号为27的字节码,然后便一直往下走完所有字节码。

    上例子中,属于在catch块发生异常,所以会看到字节码后还有一个athrow的步骤,也就是往外抛出异常啦。

     

    好了,Jvm看异常到此。

    (引:极客时间)

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-1 00:56 , Processed in 0.059472 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表