Java中的常量池;静态常量池(Static Constant Pool);运行时常量池(Runtime Constant Pool);字符串常量池(String Constant Pool)

Java中的常量池;静态常量池(Static Constant Pool);运行时常量池(Runtime Constant Pool);字符串常量池(String Constant Pool)

  • Java中的常量池
    • 静态常量池(Static Constant Pool)
    • 运行时常量池(Runtime Constant Pool)
    • 字符串常量池(String Constant Pool)
  • 总结

Java中的常量池

在 Java 中,常量池是一个用于存储常量的机制,帮助提高内存效率和运行时性能。常量池是 JVM 在编译和运行时优化的一个重要特性,它根据不同的场景可以分为 静态常量池、运行时常量池字符串常量池

下面将逐一详细解释这几种常量池。

静态常量池(Static Constant Pool)

静态常量池(编译时期) 存在于 .class 文件中,是类加载时由 JVM 解析的部分,属于类级别的常量池。它包含了常量值(例如:数字、字符串常量、枚举常量等)和一些符号引用(如类、方法、字段的引用)。静态常量池在类加载阶段就已被初始化。

主要特点:

  • 存储类的常量信息,包括:类名、字段名、方法名、常量值(如字符串和基本数据类型常量)。
  • 静态常量池在类加载时通过解析 .class 文件生成,存储在方法区(Metaspace)或永久代(对于早期的 JVM)中。
  • 静态常量池是 类级别的,每个类都会有自己的静态常量池。

示例:

public class MyClass {
    public static final int MAX_SIZE = 100;
    public static final String HELLO = "Hello, World!";
}

将Java源文件反编译成汇编语言,内容如下:

Classfile /D:/IdeaProjects/SutureMonster/sm-demo/src/main/java/com/sm/demo/Test/MyClass.class
  Last modified 2024年12月16日; size 321 bytes
  SHA-256 checksum cb02fe5b46f39d36c5ea75b334426df02c51e075ef95b2883de59c7b2de60779
  Compiled from "MyClass.java"
public class com.sm.demo.Test.MyClass
  minor version: 0
  major version: 65
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #7                          // com/sm/demo/Test/MyClass
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 2, methods: 1, attributes: 1
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               
   #6 = Utf8               ()V
   #7 = Class              #8             // com/sm/demo/Test/MyClass
   #8 = Utf8               com/sm/demo/Test/MyClass
   #9 = Utf8               MAX_SIZE
  #10 = Utf8               I
  #11 = Utf8               ConstantValue
  #12 = Integer            100
  #13 = Utf8               HELLO
  #14 = Utf8               Ljava/lang/String;
  #15 = String             #16            // Hello, World!
  #16 = Utf8               Hello, World!
  #17 = Utf8               Code
  #18 = Utf8               LineNumberTable
  #19 = Utf8               SourceFile
  #20 = Utf8               MyClass.java
{
  public static final int MAX_SIZE;
    descriptor: I
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: int 100

  public static final java.lang.String HELLO;
    descriptor: Ljava/lang/String;
    flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String Hello, World!

  public com.sm.demo.Test.MyClass();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 3: 0
}
SourceFile: "MyClass.java"

.class文件结构里的常量池(Constant pool)指的是静态常量池。
Java中的常量池;静态常量池(Static Constant Pool);运行时常量池(Runtime Constant Pool);字符串常量池(String Constant Pool)_第1张图片
可以看到 在 .class 文件中的Constant pool(静态常量池)会包含:

  • 整数常量 100,对应 MAX_SIZE
  • 字符串常量 "Hello, World!",对应 HELLO

运行时常量池(Runtime Constant Pool)

运行时常量池官网描述

运行时常量池(运行时期) 是在类加载后,JVM 在运行时为每个类创建的一个常量池,它是 静态常量池 的一个扩展。JVM 在加载类时会将 .class 文件中的静态常量池复制到运行时常量池中,然后对它进行优化、缓存等处理。运行时常量池包含了类加载后被引用的所有常量信息。

Java中基本类型的包装类的大部分都实现了常量池技术,即Byte、Short、Integer、Long、Character、Boolean、Float、Double。

如Integer会使用IntegerCache类缓存位于[-128,127]区域中的数据。好处是平时如果频繁的使用Integer,并且数值在[-128,127]中,便不会重复创建新的Integer对象,如果赋的值超出这个区域,便会创建一个新的Integer对象。

主要特点:

  • 运行时常量池是 JVM 运行时 数据区的一部分,通常存储在 方法区 中(Metaspace)。
  • 它包含了类加载时静态常量池的所有内容,并在运行时可能会添加额外的内容。
  • 运行时常量池支持动态添加,例如,当字符串常量池中的内容在程序运行时被修改时,会反映到运行时常量池中。

示例:

public class RuntimeConstantPoolExample {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "Hello";
        System.out.println(str1 == str2);  // 输出 true
    }
}

在这个例子中:

  • str1str2 都指向同一个 "Hello" 字符串常量,它们引用了运行时常量池中的 “Hello” 字符串常量。
  • str1 == str2 输出 true,因为字符串 "Hello" 存储在运行时常量池中是唯一的。

字符串常量池(String Constant Pool)

字符串常量池 是 Java 中的一种优化机制,旨在减少内存中的重复字符串对象。字符串常量池是 运行时常量池的一部分,专门用于存储字符串常量。

主要特点:

  • 唯一性:每个字符串常量池中的字符串都是唯一的。当一个字符串常量被创建时,JVM 会检查字符串常量池中是否已经存在该字符串。如果存在,则直接引用该字符串,而不是新建一个对象;如果不存在,则将其加入到池中。
  • 不可变性:字符串是不可变的,一旦创建,就无法改变。由于这一特性,字符串常量池能够安全地共享字符串对象,避免了重复创建相同内容的字符串。
  • 字符串常量池用于存储由双引号括起来的字面量字符串,如 "Hello",以及通过字符串常量引用的字符串。

工作原理:

  • 字符串字面量(如 "Hello")在编译时就被加入到字符串常量池。
  • 使用 new String("Hello") 创建的字符串并不会直接使用常量池中的对象,而是新建一个字符串对象(但引用常量池中的 "Hello")。
  • 字符串池的作用是确保在内存中只存储一份相同内容的字符串对象,从而节省内存并提高性能。

示例:

public class StringConstantPool {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = "Hello";
        String str3 = new String("Hello");

        // str1 和 str2 引用同一个对象
        System.out.println(str1 == str2); // 输出 true

        // str3 是新创建的对象,虽然内容相同,但与 str1 不同
        System.out.println(str1 == str3); // 输出 false
    }
}

在这个例子中:

  • str1str2 引用了字符串常量池中的同一个 "Hello" 对象。
  • str3 通过 new 创建了一个新的 String 对象,虽然内容相同,但它与 str1str2 不相等。

总结

  • 静态常量池:存储类的常量(字段、方法、符号引用等),在类加载时生成。
  • 运行时常量池:是 JVM 运行时使用的常量池,包含静态常量池的内容,并支持动态添加。
  • 字符串常量池:是运行时常量池的一部分,用于优化字符串常量的存储,确保相同内容的字符串只会被创建一次,从而节省内存。

这些常量池机制一起工作,共同优化了 Java 程序的内存管理,尤其是在处理大量字符串时,通过字符串常量池避免了不必要的字符串对象创建。

你可能感兴趣的:(#,JVM常用操作手册,java,常量池,静态常量池,运行时常量池,字符串常量池)