JVM_10 类加载与字节码技术(字节码指令3)

 异常处理

try-catch

public class Demo3_11_1 {
  public static void main(String[] args) {
    int i = 0;
    try {
      i = 10;
   } catch (Exception e) {
      i = 20;
   }
 }
}
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
   stack=1, locals=3, args_size=1
    0: iconst_0
    1: istore_1
    2: bipush     10
    4: istore_1
    5: goto      12
    8: astore_2
    9: bipush     20
    11: istore_1
    12: return
   Exception table:
    from   to  target type
      2   5   8  Class java/lang/Exception
   LineNumberTable: ...    
   LocalVariableTable:
    Start  Length  Slot  Name  Signature
      9    3   2   e  Ljava/lang/Exception;
      0    13   0  args  [Ljava/lang/String;
      2    11   1   i  I
   StackMapTable: ...
  MethodParameters: ...
}
  • 可以看到多出来一个 Exception table 的结构,[from, to) 是前闭后开的检测范围,一旦这个范围内的字节码执行出现异常,则通过 type 匹配异常类型,如果一致,进入 target 所指示行号
  • 8 行的字节码指令 astore_2 是将异常对象引用存入局部变量表的 slot 2 位置

多个 single-catch 块的情况 

public class Demo3_11_2 {
  public static void main(String[] args) {
    int i = 0;
    try {
因为异常出现时,只能进入 Exception table 中一个分支,所以局部变量表 slot 2 位置被共用
multi-catch 的情况
      i = 10;
   } catch (ArithmeticException e) {
      i = 30;
   } catch (NullPointerException e) {
      i = 40;
   } catch (Exception e) {
      i = 50;
   }
 }
}
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
   stack=1, locals=3, args_size=1
    0: iconst_0
    1: istore_1
    2: bipush     10
    4: istore_1
    5: goto      26
    8: astore_2
    9: bipush     30
    11: istore_1
    12: goto      26
    15: astore_2
    16: bipush     40
    18: istore_1
    19: goto      26
    22: astore_2
    23: bipush     50
    25: istore_1
    26: return
   Exception table:
    from   to  target type
      2   5   8  Class java/lang/ArithmeticException
      2   5   15  Class java/lang/NullPointerException
      2   5   22  Class java/lang/Exception
   LineNumberTable: ...
   LocalVariableTable:
    Start  Length  Slot  Name  Signature
      9    3   2   e  Ljava/lang/ArithmeticException;
     16    3   2   e  Ljava/lang/NullPointerException;
     23    3   2   e  Ljava/lang/Exception;
      0    27   0  args  [Ljava/lang/String;
      2    25   1   i  I
   StackMapTable: ...
  MethodParameters: ...
  • 因为异常出现时,只能进入 Exception table 中一个分支,所以局部变量表 slot 2 位置被共用

multi-catch 的情况 

public class Demo3_11_3 {
finally
  public static void main(String[] args) {
    try {
      Method test = Demo3_11_3.class.getMethod("test");
      test.invoke(null);
   } catch (NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
      e.printStackTrace();
   }
 }
  public static void test() {
    System.out.println("ok");
 }
}
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
   stack=3, locals=2, args_size=1
    0: ldc      #2         
    2: ldc      #3         
    4: iconst_0
    5: anewarray   #4         
    8: invokevirtual #5         
    11: astore_1
    12: aload_1
    13: aconst_null
    14: iconst_0
    15: anewarray   #6         
    18: invokevirtual #7         
    21: pop
    22: goto      30
    25: astore_1
    26: aload_1
    27: invokevirtual #11 // e.printStackTrace:()V
    30: return
   Exception table:
    from   to  target type
      0   22   25  Class java/lang/NoSuchMethodException
      0   22   25  Class java/lang/IllegalAccessException
      0   22   25  Class java/lang/reflect/InvocationTargetException
   LineNumberTable: ...
   LocalVariableTable:
    Start  Length  Slot  Name  Signature
     12    10   1  test  Ljava/lang/reflect/Method;
     26    4   1   e  Ljava/lang/ReflectiveOperationException;
      0    31   0  args  [Ljava/lang/String;

finally

public class Demo3_11_4 {
  public static void main(String[] args) {
    int i = 0;
    try {
      i = 10;
   } catch (Exception e) {
      i = 20;
   } finally {
      i = 30;
   }
 }
}
public static void main(java.lang.String[]);
  descriptor: ([Ljava/lang/String;)V
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
   stack=1, locals=4, args_size=1
    0: iconst_0
    1: istore_1   // 0 -> i
    2: bipush     10   // try --------------------------------------
    4: istore_1       // 10 -> i                 |
    5: bipush     30   // finally                 |
    7: istore_1       // 30 -> i                 |
    8: goto      27   // return -----------------------------------
    11: astore_2       // catch Exceptin -> e ----------------------
    12: bipush     20   //                     |
    14: istore_1       // 20 -> i                 |
    15: bipush     30   // finally                 |
    17: istore_1       // 30 -> i                 |
    18: goto      27   // return -----------------------------------
    21: astore_3       // catch any -> slot 3 ----------------------
    22: bipush     30   // finally                 |
    24: istore_1       // 30 -> i                 |
    25: aload_3       // <- slot 3                |
    26: athrow        // throw ------------------------------------
    27: return
   Exception table:
    from   to  target type
      2   5   11  Class java/lang/Exception
      2   5   21  any   // 剩余的异常类型,比如 Error
      11   15   21  any   // 剩余的异常类型,比如 Error
   LineNumberTable: ...
   LocalVariableTable:
    Start  Length  Slot  Name  Signature
     12    3   2   e  Ljava/lang/Exception;
      0    28   0  args  [Ljava/lang/String;
      2    26   1   i  I
   StackMapTable: ...
  MethodParameters: ...

可以看到 finally 中的代码被复制了 3 份,分别放入 try 流程,catch 流程以及 catch 剩余的异常类型流程

练习:finally面试题

finally 出现了 return

下面的题目输出什么?

public class Demo3_12_2 {
  public static void main(String[] args) {
    int result = test();
    System.out.println(result);
 }
  public static int test() {
    try {
      return 10;
   } finally {
      return 20;
   }
 }
}
 public static int test();
  descriptor: ()I
  flags: ACC_PUBLIC, ACC_STATIC
  Code:
   stack=1, locals=2, args_size=0
    0: bipush     10   // <- 10 放入栈顶
    2: istore_0       // 10 -> slot 0 (从栈顶移除了)
    3: bipush     20   // <- 20 放入栈顶
    5: ireturn       // 返回栈顶 int(20)
    6: astore_1       // catch any -> slot 1
    7: bipush     20   // <- 20 放入栈顶
    9: ireturn       // 返回栈顶 int(20)
   Exception table:
    from   to  target type
      0   3   6  any
   LineNumberTable: ...
   StackMapTable: ...
  • 由于 finally 中的 ireturn 被插入了所有可能的流程,因此返回结果肯定以 finally 的为准
  • 至于字节码中第 2 行,似乎没啥用,且留个伏笔,看下个例子
  • 跟上例中的 finally 相比,发现没有 athrow 了,这告诉我们:如果在 finally 中出现了 return,会吞掉异常,可以试一下下面的代码
.
public class Demo3_12_1 {
	public static void main(String[] args) {
		int result = test();
		System.out.println(result);
	}
	public static int test() {
		try {
			int i = 1/0;
			return 10;
		} finally {
			return 20;
		}
	}
}

 

finally 对返回值的影响

下面的题目输出什么?


public class Demo3_12_2 {
	public static void main(String[] args) {
		int result = test();
		System.out.println(result);// 10
	}
	public static int test() {
		int i = 10;
		try {
			return i;
		} finally {
			i = 20;
		}
	}
}

字节码:

public static int test();
	descriptor: ()I
	flags: ACC_PUBLIC, ACC_STATIC
	Code:
		stack=1, locals=3, args_size=0
			0: bipush 		10 		// <- 10 放入栈顶
			2: istore_0 			// 10 -> i
			3: iload_0				// <- i(10)
			4: istore_1 			// 10 -> slot 1,暂存至 slot 1,目的是为了固定返回值
			5: bipush		20 		// <- 20 放入栈顶
			7: istore_0 			// 20 -> i
			8: iload_1			    // <- slot 1(10) 载入 slot 1 暂存的值
			9: ireturn 				// 返回栈顶的 int(10)
			10: astore_2
			11: bipush 		20
			13: istore_0
			14: aload_2
			15: athrow
		Exception table:
			from to target type
			     3 5   10   any
		LineNumberTable: ...
		LocalVariableTable:
			Start Length Slot Name Signature
			   3      13    0    i         I
		StackMapTable: ...

synchronized关键字

public class Demo3_13 {
	public static void main(String[] args) {
		Object lock = new Object();
		synchronized (lock) {
			System.out.println("ok");
		}
	}
}

字节码如下:

public static void main(java.lang.String[]);
	descriptor: ([Ljava/lang/String;)V
	flags: ACC_PUBLIC, ACC_STATIC
	Code:
		stack=2, locals=4, args_size=1
			0: new 				#2 	// new Object
			3: dup
			4: invokespecial 	#1 	// invokespecial <init>:()V
			7: astore_1 			// lock引用 -> lock
			8: aload_1 				// <- lock (synchronized开始)
			9: dup
			10: astore_2 			// lock引用 -> slot 2
			11: monitorenter 		// monitorenter(lock引用)
			12: getstatic 		#3  // <- System.out
			15: ldc 				// <- "ok
			17: invokevirtual 	#5 	// invokevirtual println:
			(Ljava/lang/String;)V
			20: aload_2 			// <- slot 2(lock引用)
			21: monitorexit 		// monitorexit(lock引用)
			22: goto 			30
			25: astore_3 			// any -> slot 3
			26: aload_2 			// <- slot 2(lock引用)
			27: monitorexit 		// monitorexit(lock引用)
			28: aload_3
			29: athrow
			30: return
		Exception table:
			from to target type
			  12 22    25   any
			  25 28    25  any
		LineNumberTable: ...
		LocalVariableTable:
			Start Length Slot Name Signature
			    0     31   0  args [Ljava/lang/String;
			    8     23   1  lock Ljava/lang/Object;
		StackMapTable: ...
		MethodParameters: ...

注意:方法级别的 synchronized 不会在字节码指令中有所体现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值