好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

JVM之方法返回地址详解

JVM之方法返回地址

JVM运行时数据区的虚拟机栈的栈帧中包含了返回地址

当一个方法开始执行后,只有两种方式可以退出这个方法。

第一种方式是执行引擎遇到任意一个方法返回的字节码指令(例如:areturn),这时候可能会有返回值传递给上层的方法调用者(调用当前方法的方法称为调用者),是否有返回值和返回值的类型将根据遇到何种方法返回指令来决定,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。 另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得到处理,无论是Java虚拟机内部产生的异常,还是代码中使用 athrow 字节码指令产生的异常,只要在本方法的异常处理器表中没有搜索到匹配的异常处理器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt Method Invocation Completion)。一个方法使用异常完成出口的方式退出,是不会给它的上层调用者产生任何返回值的。 无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。

一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。

方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。

小结一下

虚拟机会使用针对每种返回类型的操作来返回,返回值将从操作数栈出栈并且入栈到调用方法的方法栈帧中,当前栈帧出栈,被调用方法的栈帧变成当前栈帧,程序计数器将重置为调用这个方法的指令的下一条指令。

方法返回地址以及栈帧中的附加信息

方法返回地址

1 点睛

存放调用该方法的 pc 寄存器的值。

一个方法的结束,有两种方式。

正常执行完成。 出现未处理的异常,非正常退出。

无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。

当一个方法开始执行后,只有两种方式可以退出这个方法。

a 执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口。

一个方法在正常调用完成之后,究竟需要使用哪一个返回指令,还需要根据方法返回值的实际数据类型而定。 在字节码指令中,返回指令包含ireturn(当返回值是boolean,byte,char,short和int类型时使用),lreturn(Long类型),freturn(Float类型),dreturn(Double类型),areturn。另外还有一个return指令声明为void的方法,实例初始化方法,类和接口的初始化方法使用。

b 在方法执行过程中遇到异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,简称异常完成出口。

方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码

2 看代码吧

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

import java.io.FileReader;

import java.io.IOException;

import java.util.Date;

/**

* 返回指令包含ireturn(当返回值是 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 解析后的结果

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

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 字节码指令到底是什么。

体会一下异常表。

      Exception table:

         from    to  target type

            4     8      11    Class java/io/IOException

本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。

正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。

一些附加信息

栈帧中还允许携带与 Java 虚拟机实现相关的一些附加信息。例如:对程序调试提供支持的信息。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

原文链接:https://HdhCmsTestjianshu测试数据/p/ecfcc9fb1de7

查看更多关于JVM之方法返回地址详解的详细内容...

  阅读:18次