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.运行时常量池具有动态性。也就是在方法区中的运行时常量池是可以发生变化的。而常量池就不行,它是静态的,当编译生成字节码文件直接就不变了。