Java中异常处理机制原理

本文详细介绍了Java异常处理的代码层面和虚拟机层面的实现机制,包括try-catch-finally语句的执行流程以及JVM的异常表。特别讨论了JDK7引入的try-with-resource语句,用于自动关闭资源并抑制finally中的异常,确保资源的有效释放,避免内存泄漏。文章还通过示例代码展示了其工作原理和使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

异常机制实现

1. 代码层面

实际代码

try
{
    //就算这里return了,也会调用finally语句,如果finally中也有return,就会抑制这个return。
}
catch (Exception e) 
{
    
}
finally 
{
    
}

类型

img

2. 虚拟机层面

异常抛出机制

  1. 显式抛出:由用户程序写代码进行抛出,必须显式捕获。 通常是Exception。
  2. 隐式抛出:由虚拟机的安全检查进行抛出,比如数组越界检查、除以0错误等等。不用用户程序捕获。通常是RunTimeError或者Error

异常捕获机制

  • 首先JVM为每一个方法生成一个异常表。包括{监控起始语句 监控终止语句 异常类型}
public static void main(String[] args) {
  try {
    mayThrowException();
  } catch (Exception e) {
    e.printStackTrace();
  }
}
// 对应的 Java 字节码
public static void main(java.lang.String[]);
  Code:
    0: invokestatic mayThrowException:()V
    3: goto 11
    6: astore_1
    7: aload_1
    8: invokevirtual java.lang.Exception.printStackTrace
   11: return
  Exception table:									// 异常表条目
    from  to target type
      0   3   6  Class java/lang/Exception  
12345678910111213141516171819

其中的from和to分别对应着监控的字节码的代码。类型就是捕获的Exception类型。

preload

  • 把finally中的代码“内联”到try和catch中各一份,或者只在catch中放一份,在try中放入跳转指令。
  • 然后如果Exception就直接进入catch语句如果遇见Error,直接执行了finally
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 -c Foo
...
  public void test();
    Code:
       0: aload_0
       1: iconst_0
       2: putfield      #20                 // Field tryBlock:I
       5: goto          30
       8: astore_1
       9: aload_0
      10: iconst_1
      11: putfield      #22                 // Field catchBlock:I
      14: aload_0
      15: iconst_2
      16: putfield      #24                 // Field finallyBlock:I
      19: goto          35
      22: astore_2
      23: aload_0
      24: iconst_2
      25: putfield      #24                 // Field finallyBlock:I
      28: aload_2
      29: athrow
      30: aload_0
      31: iconst_2
      32: putfield      #24                 // Field finallyBlock:I
      35: aload_0
      36: iconst_3
      37: putfield      #26                 // Field methodExit:I
      40: return
    Exception table:
       from    to  target type
           0     5     8   Class java/lang/Exception
           0    14    22   any
 
  ...

可以看到,编译结果包含三份 finally 代码块。其中,前两份分别位于 try 代码块和 catch 代码块的正常执行路径出口。最后一份则作为异常处理器,监控 try 代码块以及 catch 代码块。它将捕获 try 代码块触发的、未被 catch 代码块捕获的异常,以及 catch 代码块触发的异常。

3. jdk7的 try-with-resource语句的使用

背景

finally中的语句也有可能抛出错误,但是关闭资源这个错误的根本原因,可能式第一个被抛出的错误引起的,而被抛出的错误确实finally语句中的错误,从而导致了根本错误被supress。

代码层面实现

所以为了得到第一个错误,加入了Surpress机制,反映到代码层面就是try-with-resource语句。使用try-with-resource语句,会自动关闭资源,从而避免了内存泄漏的问题。另外使用try-with-resource语句,会抑制finally语句中的抛出的错误。可以使用addSupress和getSupress获取全部的Exception。

实例

@Test
public void testJdk7TryWith() {

    try(InputStream inputStream = new FileInputStream(new File("test"))) {

    } catch(Exception e) {
        e.printStackTrace();

    }
}
/*
*
*   由于第一个异常可能是根本原因,那么怎么进行捕获呢?
* */
public static void main(String[] args) throws Exception {

    InputStream in = null;
    Exception ex = null;

    try {
        in = new FileInputStream(new File("tes"));
    } catch (FileNotFoundException e) {
        ex = e;
        throw e;
    } finally {
        try {
            in.close();
        } catch (IOException e) {
            if (ex == null) {
                System.out.println("有这个文件就不抛出了");
                throw e;
            }
        } catch (NullPointerException e) { // 因为in会出现空指针,抑制第一个文件的抛出
            if (ex == null) {
                System.out.println("有这个文件就不抛出了");
            }
        }
    }
}

最后注意

使用锁,不能使用try-with语句,必须显式的调用unlock方法。代码如下

@Test
public void testLock() {
    Lock myLock = new ReentrantLock();
    try {
        myLock.lock();
    } catch (Exception e) {
        throw e;
    } finally {
        myLock.unlock();
    }
}

参考文献

JVM如何处理异常

《java核心技术卷I》异常处理/多线程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值