42.理解常量池(Class文件中的常量池与运行时常量池)

 

1.什么是常量池?

1.字节码文件中有个constant pool,就是常量池
2.当字节码文件被加载到内存中之后,方法区中会存放字节码文件的constant pool相关信息,这时候就成为了运行时常量池
在这里插入图片描述
3.常量池保存了各种字面量和对类型、域和方法的符号引用。
在这里插入图片描述
在这里插入图片描述
4.常量池可以看做是一张表,虚拟机指令根据这张表找到要执行的方法名、类名、参数类型、字面量等类型。

2.为什么需要常量池?

1.一个java源文件中的类、接口,编译后会产生一个字节码文件,而java中的字节码文件需要其他的数据支撑,通常这种数据很大,不能直接存放到字节码里面。所以把对这些数据的引用存放到常量池,在真正需要使用的时候,通过动态链接将符号引用转换为直接引用。
在这里插入图片描述
例子:

package jvm;
 
import java.io.Serializable;
 
public class MethodInnerStrucTest 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);
    }
 
    @Override
    public int compareTo(String o) {
        return 0;
    }
}

使用javap -v MethodInnerStrucTest反编译上面的java代码产生的class文件,得到下面的输出。
其中:Constant pool就是常量池。
常量池中存储的符号引用,在程序运行的时候,会被转换为直接引用。
例如:test1()方法的字节码 3: getstatic #30 // Field , #30就会引用到Constant pool里面的 #30 = Fieldref #31.#33 // ,程序运行的时候,就会直接进行转换。

Classfile /Users/simple/Documents/IBM/JVM/JVM/bin/jvm/MethodInnerStrucTest.class
  Last modified Jul 21, 2020; size 1250 bytes
  MD5 checksum 640842329ae45580b8fa6414cdc50a03
  Compiled from "MethodInnerStrucTest.java"
public class jvm.MethodInnerStrucTest 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 = Class              #2             // jvm/MethodInnerStrucTest
   #2 = Utf8               jvm/MethodInnerStrucTest
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Class              #6             // java/lang/Comparable
   #6 = Utf8               java/lang/Comparable
   #7 = Class              #8             // java/io/Serializable
   #8 = Utf8               java/io/Serializable
   #9 = Utf8               num
  #10 = Utf8               I
  #11 = Utf8               str
  #12 = Utf8               Ljava/lang/String;
  #13 = Utf8               <clinit>
 ...
{
  public int num;
    descriptor: I
    flags: ACC_PUBLIC
 ...
  
  public void test1();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=4, locals=2, args_size=1
         0: bipush        20
         2: istore_1
         3: getstatic     #30                 // Field java/lang/System.out:Ljava/io/PrintStream;
         6: new           #36                 // class java/lang/StringBuilder
         9: dup
        10: ldc           #38                 // String count =
        12: invokespecial #40                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
        15: iload_1
        16: invokevirtual #43                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        19: invokevirtual #47                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #51                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: return
      LineNumberTable:
        line 12: 0
        line 13: 3
        line 14: 25
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      26     0  this   Ljvm/MethodInnerStrucTest;
            3      23     1 count   I

  ...
}

3.运行时常量池

掌握两点:
1.常量池指的是字节码文件中的Constant pool部分。这部分内容被类加载之后,存放到方法区的运行时常量池中。
2.运行时常量池具有动态性。也就是在方法区中的运行时常量池是可以发生变化的。而常量池就不行,它是静态的,当编译生成字节码文件直接就不变了。
在这里插入图片描述

posted @ 2020-11-18 18:06  跃小云  阅读(583)  评论(0编辑  收藏  举报