JVM运行时数据区---方法区(内部结构)

方法区的内部结构

方法区存储什么?
在这里插入图片描述

《深入理解Java虚拟机》书中对方法区(Method Area)存储内容描述如下:它用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等。
在这里插入图片描述
类型信息

对每个加载的类型(类class、接口interface、枚举enum、注解annotation),JVM必须在方法区中存储以下类型信息:

  1. 这个类型的完整有效名称(全名=包名.类名);
  2. 这个类型直接父类的完整有效名(对于interface或是java.lang.Object,都没有父类);
  3. 这个类型的修饰符(public,abstract,final的某个子集);
  4. 这个类型直接接口的一个有序列表。

域(Field)信息

也就是我们常说的成员变量,域信息是比较官方的称呼。

  1. JVM必须在方法区中保存类型的所有域的相关信息以及域的声明顺序;
  2. 域的相关信息包括:域名称,域类型,域修饰符(public,private,protected,static,final,volatile,transient的某个子集)。

方法(Method)信息

JVM必须保存所有方法的以下信息,同域信息一样包括声明顺序:

  1. 方法名称;
  2. 方法的返回类型(包括 void 返回类型),void 在 Java 中对应的为 void.class;
  3. 方法参数的数量和类型(按顺序);
  4. 方法的修饰符(public,private,protected,static,final,synchronized,native,abstract的一个子集);
  5. 方法的字节码(byte codes)、操作数栈、局部变量表及大小(abstract和native方法除外);
  6. 异常表(abstract和native方法除外),异常表记录每个异常处理的开始位置、结束位置、代码处理在程序计数器中的偏移地址、被捕获的异常类的常量池索引。

举例说明

**
 * 测试方法区的内部构成
 */
 
public class MethodInnerStructTest extends Object implements Comparable<String>,Serializable {
    
    

    //属性
    public int num = 10;
    private static String str = "测试方法的内部结构";
    
    //构造器
    //方法
    public void test1(){
    
    
        int count = 20;
        System.out.println("count = " + count);
    }
    public static int test2(int cal){
    
    
        int result = 0;
        try {
    
    
            int value = 30;
            result = value / cal;
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public int compareTo(String o) {
    
    
        return 0;
    }
}
javap -v -p MethodInnerStructTest.class > test.txt
  • 反编译字节码文件,并输出值文本文件中,便于查看。参数 -p 确保能查看 private 权限类型的字段或方法。

字节码指令:

Classfile  /E:/Projects/JVM/out/production/com/heu/java/MethodInnerStructTest.class
  Last modified 2021-3-7; size 1626 bytes
  MD5 checksum 0d0fcb54854d4ce183063df985141ad0
  Compiled from "MethodInnerStructTest.java"
//类型信息      
public class com.heu.java.MethodInnerStructTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #18.#52        // java/lang/Object."<init>":()V
   #2 = Fieldref           #17.#53        // com/heu/java/MethodInnerStructTest.num:I
   #3 = Fieldref           #54.#55        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Class              #56            // java/lang/StringBuilder
   #5 = Methodref          #4.#52         // java/lang/StringBuilder."<init>":()V
   #6 = String             #57            // count =
   #7 = Methodref          #4.#58         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #4.#59         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   #9 = Methodref          #4.#60         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #61.#62        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #11 = Class              #63            // java/lang/Exception
  #12 = Methodref          #11.#64        // java/lang/Exception.printStackTrace:()V
  #13 = Class              #65            // java/lang/String
  #14 = Methodref          #17.#66        // com/heu/java/MethodInnerStructTest.compareTo:(Ljava/lang/String;)I
  #15 = String             #67            // 测试方法的内部结构
  #16 = Fieldref           #17.#68        // com/heu/java/MethodInnerStructTest.str:Ljava/lang/String;
  #17 = Class              #69            // com/heu/java/MethodInnerStructTest
  #18 = Class              #70            // java/lang/Object
  #19 = Class              #71            // java/lang/Comparable
  #20 = Class              #72            // java/io/Serializable
  #21 = Utf8               num
  #22 = Utf8               I
  #23 = Utf8               str
  #24 = Utf8               Ljava/lang/String;
  #25 = Utf8               <init>
  #26 = Utf8               ()V
  #27 = Utf8               Code
  #28 = Utf8               LineNumberTable
  #29 = Utf8               LocalVariableTable
  #30 = Utf8               this
  #31 = Utf8               Lcom/heu/java/MethodInnerStructTest;
  #32 = Utf8               test1
  #33 = Utf8               count
  #34 = Utf8               test2
  #35 = Utf8               (I)I
  #36 = Utf8               value
  #37 = Utf8               e
  #38 = Utf8               Ljava/lang/Exception;
  #39 = Utf8               cal
  #40 = Utf8               result
  #41 = Utf8               StackMapTable
  #42 = Class              #63            // java/lang/Exception
  #43 = Utf8               compareTo
  #44 = Utf8               (Ljava/lang/String;)I
  #45 = Utf8               o
  #46 = Utf8               (Ljava/lang/Object;)I
  #47 = Utf8               <clinit>
  #48 = Utf8               Signature
  #49 = Utf8               Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
  #50 = Utf8               SourceFile
  #51 = Utf8               MethodInnerStrucTest.java
  #52 = NameAndType        #25:#26        // "<init>":()V
  #53 = NameAndType        #21:#22        // num:I
  #54 = Class              #73            // java/lang/System
  #55 = NameAndType        #74:#75        // out:Ljava/io/PrintStream;
  #56 = Utf8               java/lang/StringBuilder
  #57 = Utf8               count =
  #58 = NameAndType        #76:#77        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #59 = NameAndType        #76:#78        // append:(I)Ljava/lang/StringBuilder;
  #60 = NameAndType        #79:#80        // toString:()Ljava/lang/String;
  #61 = Class              #81            // java/io/PrintStream
  #62 = NameAndType        #82:#83        // println:(Ljava/lang/String;)V
  #63 = Utf8               java/lang/Exception
  #64 = NameAndType        #84:#26        // printStackTrace:()V
  #65 = Utf8               java/lang/String
  #66 = NameAndType        #43:#44        // compareTo:(Ljava/lang/String;)I
  #67 = Utf8               测试方法的内部结构
  #68 = NameAndType        #23:#24        // str:Ljava/lang/String;
  #69 = Utf8               com/heu/java/MethodInnerStructTest
  #70 = Utf8               java/lang/Object
  #71 = Utf8               java/lang/Comparable
  #72 = Utf8               java/io/Serializable
  #73 = Utf8               java/lang/System
  #74 = Utf8               out
  #75 = Utf8               Ljava/io/PrintStream;
  #76 = Utf8               append
  #77 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #78 = Utf8               (I)Ljava/lang/StringBuilder;
  #79 = Utf8               toString
  #80 = Utf8               ()Ljava/lang/String;
  #81 = Utf8               java/io/PrintStream
  #82 = Utf8               println
  #83 = Utf8               (Ljava/lang/String;)V
  #84 = Utf8               printStackTrace
{
    
    
//域信息
  public int num;
    descriptor: I
    flags: ACC_PUBLIC

  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC

  //方法信息
  public com.heu.java.MethodInnerStructTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        10
         7: putfield      #2                  // Field num:I
        10: return
      LineNumberTable:
        line 10: 0
        line 12: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/heu/java/MethodInnerStructTest;

  public void test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=1
         0: bipush        20
         2: istore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #4                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        13: ldc           #6                  // String count =
        15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: iload_1
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 17: 0
        line 18: 3
        line 19: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   Lcom/heu/java/MethodInnerStructTest;
            3      26     1 count   I

  public static int test2(int);
    descriptor: (I)I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: iconst_0
         1: istore_1
         2: bipush        30
         4: istore_2
         5: iload_2
         6: iload_0
         7: idiv
         8: istore_1
         9: goto          17
        12: astore_2
        13: aload_2
        14: invokevirtual #12                 // Method java/lang/Exception.printStackTrace:()V
        17: iload_1
        18: ireturn
      Exception table:
         from    to  target type
             2     9    12   Class java/lang/Exception
      LineNumberTable:
        line 21: 0
        line 23: 2
        line 24: 5
        line 27: 9
        line 25: 12
        line 26: 13
        line 28: 17
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            5       4     2 value   I
           13       4     2     e   Ljava/lang/Exception;
            0      19     0   cal   I
            2      17     1 result   I
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 12
          locals = [ int, int ]
          stack = [ class java/lang/Exception ]
        frame_type = 4 /* same */

  public int compareTo(java.lang.String);
    descriptor: (Ljava/lang/String;)I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iconst_0
         1: ireturn
      LineNumberTable:
        line 33: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       2     0  this   Lcom/heu/java/MethodInnerStructTest;
            0       2     1     o   Ljava/lang/String;

  public int compareTo(java.lang.Object);
    descriptor: (Ljava/lang/Object;)I
    flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: checkcast     #13                 // class java/lang/String
         5: invokevirtual #14                 // Method compareTo:(Ljava/lang/String;)I
         8: ireturn
      LineNumberTable:
        line 10: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       9     0  this   Lcom/heu/java/MethodInnerStructTest;

  static {
    
    };
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: ldc           #15                 // String 测试方法的内部结构
         2: putstatic     #16                 // Field str:Ljava/lang/String;
         5: return
      LineNumberTable:
        line 13: 0
}
Signature: #49                          // Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/io/Serializable;
SourceFile: "MethodInnerStrucTest.java"

类型信息:在运行时方法区中,类信息中记录了哪个加载器加载了该类,同时类加载器也记录了它加载了哪些类。

//类型信息      
public class com.heu.java.MethodInnerStrucTest extends java.lang.Object implements java.lang.Comparable<java.lang.String>, java.io.Serializable

域信息:

扫描二维码关注公众号,回复: 13026799 查看本文章
  1. descriptor: I 表示字段类型为 Integer;
  2. flags: ACC_PUBLIC 表示字段权限修饰符为 public。
//域信息
  public int num;
    descriptor: I
    flags: ACC_PUBLIC

  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC

方法信息:

  1. descriptor: ()V 表示方法返回值类型为 void;
  2. flags: ACC_PUBLIC 表示方法权限修饰符为 public;
  3. stack=3 表示操作数栈深度为 3;
  4. locals=2 表示局部变量个数为 2 个(实例方法包含 this);
  5. test1() 方法虽然没有参数,但是其 args_size=1 ,这时因为将 this 作为了参数。
public void test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=2, args_size=1
         0: bipush        20
         2: istore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #4                  // class java/lang/StringBuilder
         9: dup
        10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
        13: ldc           #6                  // String count =
        15: invokevirtual #7                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        18: iload_1
        19: invokevirtual #8                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        22: invokevirtual #9                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        25: invokevirtual #10                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        28: return
      LineNumberTable:
        line 17: 0
        line 18: 3
        line 19: 28
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      29     0  this   Lcom/heu/java/MethodInnerStructTest;
            3      26     1 count   I

non-final 类型的类(静态)变量

  • 静态变量和类关联在一起,随着类的加载而加载,它们成为类数据在逻辑上的一部分;
  • 类变量(静态变量)被类的所有实例共享,即使没有类实例时,也可以访问它。

举例说明:

  1. 如下代码所示,即使我们把order设置为null,也不会出现空指针异常;
  2. 这更加表明了 static 类型的字段和方法随着类的加载而加载,并不属于特定的类实例。
public class MethodAreaTest {
    
    
    public static void main(String[] args) {
    
    
        Order order = null;
        order.hello();
        System.out.println(order.count);
    }
}

class Order {
    
    
    public static int count = 1;
    public static final int number = 2;


    public static void hello() {
    
    
        System.out.println("hello!");
    }
}

输出结果:

hello!
1

全局常量:static final

  1. 全局常量就是使用 static final 进行修饰;
  2. 被声明为 final 的类变量的处理方法则不同,每个全局常量在编译的时候就会被分配了。

举例说明:

class Order {
    
    
    public static int count = 1;
    public static final int number = 2;
    ...
}    

查看上面代码的字节码指令:

public static int count;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC

  public static final int number;
    descriptor: I
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 2

可以发现 staitc和final同时修饰的number 的值在编译上的时候已经写死在字节码文件中了。

总结:
在这里插入图片描述

运行时常量池

运行时常量池 VS 常量池

官方文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

在这里插入图片描述

  1. 方法区,内部包含了运行时常量池;
  2. 字节码文件,内部包含了常量池。(之前的字节码文件中已经看到了很多 Constant pool的东西,这个就是常量池);
  3. 要弄清楚方法区,需要理解清楚 Class File ,因为加载类的信息都在方法区;
  4. 要弄清楚方法区的运行时常量池,需要理解清楚 Class File 中的常量池。

常量池

  1. 一个有效的字节码文件中除了包含类的版本信息、字段、方法以及接口等描述符信息外。还包含一项信息就是常量池表(Constant Pool Table),包括各种字面量和对类型、域和方法的符号引用;
  2. 字面量: 10 , “我是某某” ,这种数字和字符串都是字面量。
    在这里插入图片描述

在这里插入图片描述
为什么需要常量池?

  • 一个java源文件中的类、接口,编译后产生一个字节码文件,而Java中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,因此换另一种方式,可以存到常量池。字节码包含了指向常量池的引用,在动态链接的时候会用到运行时常量池(类的加载过程中有讲)。

比如下面代码:

public class SimpleClass {
    
    
    public void sayHello() {
    
    
        System.out.println("hello");
    }
}
  • 虽然上述代码很小,但是里面却使用了String、System、PrintStream及Object等结构;
  • 这个文件中有6个地方用到了 ”hello” 这个字符串,如果不用常量池,就需要在6个地方全写一遍,造成臃肿。可以将 ”hello” 等所需用到的结构信息记录在常量池中,并通过引用的方式,来加载、调用所需的结构;
  • 这里的代码量其实很少了,如果代码多的话,引用的结构将会更多,这里就需要用到常量池了。

常量池中有什么?

  1. 数量值
  2. 字符串值
  3. 类引用
  4. 字段引用
  5. 方法引用

上面的 MethodInnerStructTest 的 test1方法的字节码如下:

 0 bipush 20
 2 istore_1
 3 getstatic #3 <java/lang/System.out>
 6 new #4 <java/lang/StringBuilder>
 9 dup
10 invokespecial #5 <java/lang/StringBuilder.<init>>
13 ldc #6 <count = >
15 invokevirtual #7 <java/lang/StringBuilder.append>
18 iload_1
19 invokevirtual #8 <java/lang/StringBuilder.append>
22 invokevirtual #9 <java/lang/StringBuilder.toString>
25 invokevirtual #10 <java/io/PrintStream.println>
28 return

1、#3,#5等等这些带# 的,都是引用了常量池。

总结:常量池、可以看做是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等类型。

运行时常量池

  • 运行时常量池(Runtime Constant Pool)是方法区的一部分;
  • 常量池表(Constant Pool Table)是 Class 字节码文件的一部分,用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。(运行时常量池就是常量池在程序运行时的称呼);
  • 运行时常量池,在加载类和接口到虚拟机后,就会创建对应的运行时常量池;
  • JVM 为每个已加载的类型(类或接口)都维护一个常量池。池中的数据项像数组项一样,是通过索引访问的;
  • 运行时常量池中包含多种不同的常量,包括编译期就已经明确的数值字面量,也包括运行期解析后才能够获得的方法或者字段引用。此时不再是常量池中的符号地址了,这里换为真实地址。而运行时常量池,相对于Class文件常量池的另一重要特征是:具备动态性;
  • 运行时常量池类似于传统编程语言中的符号表(symbol table),但是它所包含的数据却比符号表要更加丰富一些;
  • 当创建类或接口的运行时常量池时,如果构造运行时常量池所需的内存空间超过了方法区所能提供的最大值,则JVM会抛OutofMemoryError异常。

方法区的使用举例

示例代码:

public class MethodAreaDemo {
    
    
    public static void main(String[] args) {
    
    
        int x = 500;
        int y = 100;
        int a = x / y;
        int b = 50;
        System.out.println(a + b);
    }
}

对应字节码:

public class com.heu.java.MethodAreaDemo
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#24         // java/lang/Object."<init>":()V
   #2 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #4 = Class              #29            // com/heu/java/MethodAreaDemo
   #5 = Class              #30            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/heu/java/MethodAreaDemo;
  #13 = Utf8               main
  #14 = Utf8               ([Ljava/lang/String;)V
  #15 = Utf8               args
  #16 = Utf8               [Ljava/lang/String;
  #17 = Utf8               x
  #18 = Utf8               I
  #19 = Utf8               y
  #20 = Utf8               a
  #21 = Utf8               b
  #22 = Utf8               SourceFile
  #23 = Utf8               MethodAreaDemo.java
  #24 = NameAndType        #6:#7          // "<init>":()V
  #25 = Class              #31            // java/lang/System
  #26 = NameAndType        #32:#33        // out:Ljava/io/PrintStream;
  #27 = Class              #34            // java/io/PrintStream
  #28 = NameAndType        #35:#36        // println:(I)V
  #29 = Utf8               com/heu/java/MethodAreaDemo
  #30 = Utf8               java/lang/Object
  #31 = Utf8               java/lang/System
  #32 = Utf8               out
  #33 = Utf8               Ljava/io/PrintStream;
  #34 = Utf8               java/io/PrintStream
  #35 = Utf8               println
  #36 = Utf8               (I)V
{
    
    
  public com.heu.java.MethodAreaDemo();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/heu/java/MethodAreaDemo;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=5, args_size=1
         0: sipush        500
         3: istore_1
         4: bipush        100
         6: istore_2
         7: iload_1
         8: iload_2
         9: idiv
        10: istore_3
        11: bipush        50
        13: istore        4
        15: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        18: iload_3
        19: iload         4
        21: iadd
        22: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        25: return
      LineNumberTable:
        line 9: 0
        line 10: 4
        line 11: 7
        line 12: 11
        line 13: 15
        line 14: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  args   [Ljava/lang/String;
            4      22     1     x   I
            7      19     2     y   I
           11      15     3     a   I
           15      11     4     b   I
}
SourceFile: "MethodAreaDemo.java"

图解字节码指令执行流程:

1、初始状态;
在这里插入图片描述

2、首先将操作数500压入操作数栈中;
在这里插入图片描述

3、然后操作数 500 从操作数栈中取出,存储到局部变量表中索引为 1 的位置;
在这里插入图片描述

4、重复一次,把100压如操作数栈中,之后放入局部变量表中,最后再将变量表中的500 和 100 取出,进行操作;
在这里插入图片描述

5、将500 和 100 进行一个除法运算,再把结果入栈;
在这里插入图片描述
接着50入栈出栈,并将其保存在局部变量4中;
在这里插入图片描述

6、将50和5 压入操作数栈,并执行加法操作;
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
7、最后调用 invokevirtual(虚方法调用),进行打印,然后返回。
在这里插入图片描述
返回时:
在这里插入图片描述
程序计数器始终计算的都是当前代码运行的位置,目的是为了方便记录 方法调用后能够正常返回,或者是进行了 CPU 切换后,也能回来到原来的代码进行执行。

符号引用 –> 直接饮用

  1. 上面代码调用 System.out.println() 方法时,首先需要看看 System 类有没有加载,再看看 PrintStream 类有没有加载;
  2. 如果没有加载,则执行加载,执行时,将常量池中的符号引用(字面量)转换为运行时常量池的直接引用(真正的地址值)。

猜你喜欢

转载自blog.csdn.net/qq_33626996/article/details/114486006