JVM之方法返回地址詳解
JVM之方法返回地址
JVM運行時數(shù)據(jù)區(qū)的虛擬機棧的棧幀中包含了返回地址
當(dāng)一個方法開始執(zhí)行后,只有兩種方式可以退出這個方法。
- 第一種方式是執(zhí)行引擎遇到任意一個方法返回的字節(jié)碼指令(例如:areturn),這時候可能會有返回值傳遞給上層的方法調(diào)用者(調(diào)用當(dāng)前方法的方法稱為調(diào)用者),是否有返回值和返回值的類型將根據(jù)遇到何種方法返回指令來決定,這種退出方法的方式稱為正常完成出口(Normal Method Invocation Completion)。
- 另外一種退出方式是,在方法執(zhí)行過程中遇到了異常,并且這個異常沒有在方法體內(nèi)得到處理,無論是Java虛擬機內(nèi)部產(chǎn)生的異常,還是代碼中使用 athrow 字節(jié)碼指令產(chǎn)生的異常,只要在本方法的異常處理器表中沒有搜索到匹配的異常處理器,就會導(dǎo)致方法退出,這種退出方法的方式稱為異常完成出口(Abrupt Method Invocation Completion)。一個方法使用異常完成出口的方式退出,是不會給它的上層調(diào)用者產(chǎn)生任何返回值的。
- 無論采用何種退出方式,在方法退出之后,都需要返回到方法被調(diào)用的位置,程序才能繼續(xù)執(zhí)行,方法返回時可能需要在棧幀中保存一些信息,用來幫助恢復(fù)它的上層方法的執(zhí)行狀態(tài)。
一般來說,方法正常退出時,調(diào)用者的程序計數(shù)器的值可以作為返回地址,棧幀中很可能會保存這個計數(shù)器值。而方法異常退出時,返回地址是要通過異常處理器表來確定的,棧幀中一般不會保存這部分信息。
方法退出的過程實際上就等同于把當(dāng)前棧幀出棧,因此退出時可能執(zhí)行的操作有:恢復(fù)上層方法的局部變量表和操作數(shù)棧,把返回值(如果有的話)壓入調(diào)用者棧幀的操作數(shù)棧中,調(diào)整程序計數(shù)器的值以指向方法調(diào)用指令后面的一條指令等。
小結(jié)一下
虛擬機會使用針對每種返回類型的操作來返回,返回值將從操作數(shù)棧出棧并且入棧到調(diào)用方法的方法棧幀中,當(dāng)前棧幀出棧,被調(diào)用方法的棧幀變成當(dāng)前棧幀,程序計數(shù)器將重置為調(diào)用這個方法的指令的下一條指令。
方法返回地址以及棧幀中的附加信息
方法返回地址
1 點睛
存放調(diào)用該方法的 pc 寄存器的值。
一個方法的結(jié)束,有兩種方式。
- 正常執(zhí)行完成。
- 出現(xiàn)未處理的異常,非正常退出。
無論通過哪種方式退出,在方法退出后都返回到該方法被調(diào)用的位置。方法正常退出時,調(diào)用者的pc計數(shù)器的值作為返回地址,即調(diào)用該方法的指令的下一條指令的地址。而通過異常退出的,返回地址是要通過異常表來確定,棧幀中一般不會保存這部分信息。
當(dāng)一個方法開始執(zhí)行后,只有兩種方式可以退出這個方法。
a 執(zhí)行引擎遇到任意一個方法返回的字節(jié)碼指令(return),會有返回值傳遞給上層的方法調(diào)用者,簡稱正常完成出口。
- 一個方法在正常調(diào)用完成之后,究竟需要使用哪一個返回指令,還需要根據(jù)方法返回值的實際數(shù)據(jù)類型而定。
- 在字節(jié)碼指令中,返回指令包含ireturn(當(dāng)返回值是boolean,byte,char,short和int類型時使用),lreturn(Long類型),freturn(Float類型),dreturn(Double類型),areturn。另外還有一個return指令聲明為void的方法,實例初始化方法,類和接口的初始化方法使用。
b 在方法執(zhí)行過程中遇到異常(Exception),并且這個異常沒有在方法內(nèi)進行處理,也就是只要在本方法的異常表中沒有搜索到匹配的異常處理器,就會導(dǎo)致方法退出,簡稱異常完成出口。
方法執(zhí)行過程中,拋出異常時的異常處理,存儲在一個異常處理表,方便在發(fā)生異常的時候找到處理異常的代碼
2 看代碼吧
import java.io.FileReader; import java.io.IOException; import java.util.Date; /** * 返回指令包含ireturn(當(dāng)返回值是 boolean、byte、char、short和int類型時使用)、 * lreturn、freturn、dreturn以及areturn, * 另外還有一個return指令供聲明為void的方法、 * 實例初始化方法、類和接口的初始化方法使用。 */ public class ReturnAddressTest { public boolean methodBoolean() { return false; } public byte methodByte() { return 0; } public short methodShort() { return 0; } public char methodChar() { return 'a'; } public int methodInt() { return 0; } public long methodLong() { return 0L; } public float methodFloat() { return 0.0f; } public double methodDouble() { return 0.0; } public String methodString() { return null; } public Date methodDate() { return null; } public void methodVoid() { } static { int i = 10; } public void method2() { methodVoid(); try { method1(); } catch (IOException e) { e.printStackTrace(); } } public void method1() throws IOException { FileReader fis = new FileReader("atguigu.txt"); char[] cBuffer = new char[1024]; int len; while ((len = fis.read(cBuffer)) != -1) { String str = new String(cBuffer, 0, len); System.out.println(str); } fis.close(); } }
3 解析后的結(jié)果
public boolean methodBoolean(); descriptor: ()Z flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 15: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public byte methodByte(); descriptor: ()B flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 19: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public short methodShort(); descriptor: ()S flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 23: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public char methodChar(); descriptor: ()C flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: bipush 97 2: ireturn LineNumberTable: line 27: 0 LocalVariableTable: Start Length Slot Name Signature 0 3 0 this Lcom/atguigu/java3/ReturnAddressTest; public int methodInt(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 31: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public long methodLong(); descriptor: ()J flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: lconst_0 1: lreturn LineNumberTable: line 35: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public float methodFloat(); descriptor: ()F flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: fconst_0 1: freturn LineNumberTable: line 39: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public double methodDouble(); descriptor: ()D flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: dconst_0 1: dreturn LineNumberTable: line 43: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public java.lang.String methodString(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn LineNumberTable: line 47: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public java.util.Date methodDate(); descriptor: ()Ljava/util/Date; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn LineNumberTable: line 51: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public void methodVoid(); descriptor: ()V flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 56: 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcom/atguigu/java3/ReturnAddressTest; public void method2(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=1 0: aload_0 1: invokevirtual #2 // Method methodVoid:()V 4: aload_0 5: invokevirtual #3 // Method method1:()V 8: goto 16 11: astore_1 12: aload_1 13: invokevirtual #5 // Method java/io/IOException.printStackTrace:()V 16: return Exception table: from to target type 4 8 11 Class java/io/IOException LineNumberTable: line 63: 0 line 65: 4 line 68: 8 line 66: 11 line 67: 12 line 69: 16 LocalVariableTable: Start Length Slot Name Signature 12 4 1 e Ljava/io/IOException; 0 17 0 this Lcom/atguigu/java3/ReturnAddressTest; StackMapTable: number_of_entries = 2 frame_type = 75 /* same_locals_1_stack_item */ stack = [ class java/io/IOException ] frame_type = 4 /* same */ public void method1() throws java.io.IOException; descriptor: ()V flags: ACC_PUBLIC Code: stack=5, locals=5, args_size=1 0: new #6 // class java/io/FileReader 3: dup 4: ldc #7 // String atguigu.txt 6: invokespecial #8 // Method java/io/FileReader."<init>":(Ljava/lang/String;)V 9: astore_1 10: sipush 1024 13: newarray char 15: astore_2 16: aload_1 17: aload_2 18: invokevirtual #9 // Method java/io/FileReader.read:([C)I 21: dup 22: istore_3 23: iconst_m1 24: if_icmpeq 50 27: new #10 // class java/lang/String 30: dup 31: aload_2 32: iconst_0 33: iload_3 34: invokespecial #11 // Method java/lang/String."<init>":([CII)V 37: astore 4 39: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 42: aload 4 44: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 47: goto 16 50: aload_1 51: invokevirtual #14 // Method java/io/FileReader.close:()V 54: return LineNumberTable: line 72: 0 line 73: 10 line 75: 16 line 76: 27 line 77: 39 line 78: 47 line 79: 50 line 80: 54 LocalVariableTable: Start Length Slot Name Signature 39 8 4 str Ljava/lang/String; 0 55 0 this Lcom/atguigu/java3/ReturnAddressTest; 10 45 1 fis Ljava/io/FileReader; 16 39 2 cBuffer [C 23 32 3 len I StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 16 locals = [ class java/io/FileReader, class "[C" ] frame_type = 252 /* append */ offset_delta = 33 locals = [ int ] Exceptions: throws java.io.IOException static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=1, args_size=0 0: bipush 10 2: istore_0 3: return LineNumberTable: line 59: 0 line 60: 3 LocalVariableTable: Start Length Slot Name Signature }
說明
可以觀察一下各種方法的 return 字節(jié)碼指令到底是什么。
體會一下異常表。
Exception table:
from to target type
4 8 11 Class java/io/IOException
本質(zhì)上,方法的退出就是當(dāng)前棧幀出棧的過程。此時,需要恢復(fù)上層方法的局部變量表、操作數(shù)棧、將返回值壓入調(diào)用者棧幀的操作數(shù)棧、設(shè)置PC寄存器值等,讓調(diào)用者方法繼續(xù)執(zhí)行下去。
正常完成出口和異常完成出口的區(qū)別在于:通過異常完成出口退出的不會給他的上層調(diào)用者產(chǎn)生任何的返回值。
一些附加信息
棧幀中還允許攜帶與 Java 虛擬機實現(xiàn)相關(guān)的一些附加信息。例如:對程序調(diào)試提供支持的信息。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
淺談Java內(nèi)部類與靜態(tài)內(nèi)部類的區(qū)別
本文主要介紹了淺談Java內(nèi)部類與靜態(tài)內(nèi)部類的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Springmvc基于fastjson實現(xiàn)導(dǎo)包及配置文件
這篇文章主要介紹了Springmvc基于fastjson實現(xiàn)導(dǎo)包及配置文件,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-10-10Java自定義類數(shù)組報null的相關(guān)問題及解決
這篇文章主要介紹了Java自定義類數(shù)組報null的相關(guān)問題及解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09Spring?Boot?Admin?添加報警提醒和登錄驗證功能的具體實現(xiàn)
報警提醒功能是基于郵箱實現(xiàn)的,當(dāng)然也可以使用其他的提醒功能,如釘釘或飛書機器人提醒也是可以的,但郵箱報警功能的實現(xiàn)成本最低,所以本文我們就來看郵箱的報警提醒功能的具體實現(xiàn)2022-01-01