浅谈 JAVA 常量池

       首先,理解下啥子叫常量池, 常量池是一个放入数据的一个堆(JDK1.8中), 如果是重新创建一个相同的数据,则把引用指向常量池. 大致可以这么理解.

       常量池要划分的话,那么可以划分为静态常量池和运行时常量池。注意:静态常量池再运行时也会写入到运行时的常量池

        静态常量池: 指的是再编译的过程中,就能确定下来值的,都可以称为静态常量池,class常量池不仅仅包括字符和数字这些/字面量。比如我们用final 修饰的基本类型,或者用String s1 = "a"; 等  看如下代码:

package com.jvm.classloading;

public class MyTest2 {

    public static void main(String[] args) {
        String str = "abc";
        System.out.println(str);
        System.out.println(MyParent2.str);
    }
}

class MyParent2{
    public static final String str = "hello word";

    static {
        System.out.println("MyParent2 static blok");
    }
}

 

这段代码中,MyParent2 这个类不会初始化,因为没有设计到主动调用,static模块不会被调用.如果要了解类加载,请看我的文章

所以输出的结果为

abc
hello word

然后我们在使用javap 看下静态常量池是如何做的

Classfile /C:/Users/able/IdeaProjects/jvm/out/production/jvm/com/jvm/classloading/MyTest2.class
  Last modified 2020-4-7; size 665 bytes
  MD5 checksum bc4de9eac30a5e4f9ecc4029ddacf12f
  Compiled from "MyTest2.java"
public class com.jvm.classloading.MyTest2
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#24         // java/lang/Object."<init>":()V
   #2 = String             #25            // abc
   #3 = Fieldref           #26.#27        // java/lang/System.out:Ljava/io/PrintStream;
   #4 = Methodref          #28.#29        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #30            // com/jvm/classloading/MyParent2
   #6 = String             #31            // hello word
   #7 = Class              #32            // com/jvm/classloading/MyTest2
   #8 = Class              #33            // java/lang/Object
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = Utf8               Code
  #12 = Utf8               LineNumberTable
  #13 = Utf8               LocalVariableTable
  #14 = Utf8               this
  #15 = Utf8               Lcom/jvm/classloading/MyTest2;
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               args
  #19 = Utf8               [Ljava/lang/String;
  #20 = Utf8               str
  #21 = Utf8               Ljava/lang/String;
  #22 = Utf8               SourceFile
  #23 = Utf8               MyTest2.java
  #24 = NameAndType        #9:#10         // "<init>":()V
  #25 = Utf8               abc
  #26 = Class              #34            // java/lang/System
  #27 = NameAndType        #35:#36        // out:Ljava/io/PrintStream;
  #28 = Class              #37            // java/io/PrintStream
  #29 = NameAndType        #38:#39        // println:(Ljava/lang/String;)V
  #30 = Utf8               com/jvm/classloading/MyParent2
  #31 = Utf8               hello word
  #32 = Utf8               com/jvm/classloading/MyTest2
  #33 = Utf8               java/lang/Object
  #34 = Utf8               java/lang/System
  #35 = Utf8               out
  #36 = Utf8               Ljava/io/PrintStream;
  #37 = Utf8               java/io/PrintStream
  #38 = Utf8               println
  #39 = Utf8               (Ljava/lang/String;)V
{
  public com.jvm.classloading.MyTest2();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/jvm/classloading/MyTest2;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
         6: aload_1
         7: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: getstatic     #3                  // Field java/lang/System.out:Ljava/io/PrintStream;
        13: ldc           #6                  // String hello word
        15: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        18: return
      LineNumberTable:
        line 6: 0
        line 7: 3
        line 8: 10
        line 9: 18
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      19     0  args   [Ljava/lang/String;
            3      16     1   str   Ljava/lang/String;
}
SourceFile: "MyTest2.java"

我们先来看下 String str = "abc";

0: ldc           #2                  // String abc

找到这行jvm编译的. 这里使用的是符号引用,如果想了解符号引用,观看符号引用和直接引用

根据#2 我们最后找到

#25 = Utf8               abc

这里已经在编译的时候,就把abc给写入进去

同理在看下

System.out.println(MyParent2.str);

输出代码我们先不管, 我们看到

13: ldc           #6                  // String hello word

这行代码,最后的解析为

#31 = Utf8               hello word

在生成的class文件中,已经把我们定义的变量写入进去.这里我就可以理解为静态常量池。 同理 基本类型也是一样,能在运行期间确定的值,都在编译时,写入class文件中。当我们在运行时,也会把这些值写入堆内存常量池中

-------------------------------------------------------分割线----------------------------------------------------------------------------------------------------------------------

 

下面我们在说下 运行时常量池

运行时常量池: 虚拟机会将在类加载后把各个class文件中的常量池载入运行时常量池中,前面的静态常量池只是一个静态文件结构,运行时常量池也是方法区的一部分(JDK1.6之前),是一块内存区域.运行时常量池可以在运行期间符号引用解析为直接引用

即把那些描述符替换为能直接定位到字段,方法的引用和句柄.

 

 

后面再说说字符串常量池,整数常量池.

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值