JVM字符串常量池

字符串常量池

在JDK7之后,字符串常量池从运行时常量池中分离,放到堆中。

String::intern如果存在堆中的对象,会直接保存对象的引用,而不会重新创建对象,否则在字符串常量池创建对象。

以下代码分析基于JDK7以上

public static void main(String[] args) {
    String s = new String("1");//字符串常量池由于class文件的加载,故有"1",堆区创建一个对象为"1",但s指向的是堆区的"1"而非常量池的"1"
    s.intern();//尝试将"1"放入堆区中的字符串常量池,但此时已有,故返回字符串常量池中的引用
    String s2 = "1";//默认先找字符串常量池有无"1",此时有则直接指向常量池中的"1"
    System.out.println(s == s2);//false

    String s3 = new String("1") + new String("1");//堆区新增两个"1"对象(共3个不在字符串常量池中的"1"),和一个"11"对象,但字符串常量池    没有       没有      没有     "11"!!!
    String s5 = s3.intern();//查找常量池中是否有"11",此时没有,直接保存堆中的"11"的s3引用!!!到字符串常量池;
    System.out.println(s3 == s5);//true,s3、s5均指向堆区的"11";
    String s4 = "11";//默认先找字符串常量池有无"11",此时常量池直接是堆区的"11"引用,故s4指向的是s3
    System.out.println(s3 == s4);//true
}

在这里插入图片描述
图中的双箭头表示引用变量实际指向的内存。

public static void main(String[] args) {
    String s = new String("1");//字符串常量池在加载class的时候已经有一个"1",堆创建一个"1",s指向堆中的"1"
    String s2 = "1";//s2指向字符串常量池中的"1"
    s.intern();//查找字符串常量池中是否有"1",此时有则不创建
    System.out.println(s == s2);//false

    String s3 = new String("1") + new String("1");//堆中新增两个匿名"1",和一个"11",s3指向堆中的"11"
    String s4 = "11";//字符串常量池新增一个"11"由s4指向
    s3.intern();//查找字符串常量池中是否有"11",此时有则不创建
    System.out.println(s3 == s4);//false
}

综上:

  1. 过程:加载class文件(含静态常量池);将静态常量池放入方法区中的运行时常量池;读取到某个字面量str,如果字符串常量池中没有对应的引用,则在堆中创建字面量str,字符串常量池保存str的引用,否则不动。
    字符串常量池可以认为是通过堆来保存池中的字符串常量,它自身维护一个哈希表(保留引用)
  2. 能够向字符串常量池添加字符串常量只有两种方式:
    1. 懒加载字面量:运行时执行到具体的代码处扫描到字面量,才会向字符串常量池中添加对象。
    2. String::intern(),不再在堆中创建对象,直接保存对应的引用到字符串常量池中。
  3. 如果字符串常量池内已有,则直接返回引用,否则在堆中new一个,再返回引用。
发布了24 篇原创文章 · 获赞 0 · 访问量 989

猜你喜欢

转载自blog.csdn.net/deltapluskai/article/details/104350654