【JVM 07-运行时常量池重要组成部分-StringTable】

StringTable 笔记记录

  • [1. 常量池、运行时常量池与字符串常量池(StringTable)的关系](#1. 常量池、运行时常量池与字符串常量池(StringTable)的关系)
  • [2. String str="a"放入字符串常量池的过程](#2. String str="a"放入字符串常量池的过程)
  • [3. 常见面试题](#3. 常见面试题)
  • [4. StringTable特性](#4. StringTable特性)
  • 5.StringTable的位置变更
    • [5.1 为什么位置变换?](#5.1 为什么位置变换?)
    • [5.2 位置变更演示](#5.2 位置变更演示)
  • [6. StringTable垃圾回收](#6. StringTable垃圾回收)
  • [7. StringTable性能调优](#7. StringTable性能调优)
    • [7.1 考虑字符串是否入池(字符串常量池)](#7.1 考虑字符串是否入池(字符串常量池))

1. 常量池、运行时常量池与字符串常量池(StringTable)的关系

java 复制代码
/**
 * StringTable[] 当变为字符串对象时,还会将符号当作key在StringTable中去找,看有没有取值相同的key,
 * 如果没有就放入,如果有就直接使用。
 * 也就是StringTable结构其实是一个哈希表。哈希表是长度固定的,不能进行扩容。如果没有"a"字符串对象,则
 * 会放入串池。StringTable ["a"],执行完String s1="a"时就会放入串池中。下一行代码类似的。
 **/
public class Demo {
    //常量池中的信息,都会被加载到运行时常量池中,这时a,b,ab,都是常量池中的符号,还没有变为java字符串对象
    //当执行到该行代码时:
    //ldc #2 会把a符号变为 "a" 字符串对象
    //ldc #3 会把b符号变为 "b" 字符串对象
    //ldc #4 会把ab符号变为"ab" 字符串对象
    public static void main(String[] args) {
        //将"a"放入字符串池中是一个惰性的过程,当执行到该行代码时,才会去检查是否有。
        String s1="a";
        String s2="b";
        String s3="ab";
    }
}

使用javap -v class文件看具体细节。

这里能看到Constant poool;

常量池:.class 文件中的静态数据,存储字面量和符号引用。
运行时常量池:类加载后解析的常量池,支持动态修改。
字符串常量池:运行时常量池的子集,专门存储字符串字面量。

2. String str="a"放入字符串常量池的过程

3. 常见面试题

java 复制代码
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        //new  StringBuilder().append("a").append("b").toString;
        //StringBuilder的toString方法最终是new String("ab");
        String s4 = s1+s2; 
        String s5="a"+"b";
        System.out.println(s3==s4);//false s3在字符串常量池中,s4在堆中,地址不一样。
        System.out.println(s3==s5);//true 编译器优化 实际上是"ab"  这里都可以利用javap -v *.class 查看字节码

String s1 = "a";

String s2 = "b";

String s4 = s1+s2;的底层如下

4. StringTable特性

  1. 常量池中的字符串仅是符号,第一次用到时才变为对象。
  2. 利用串池的机制,避免重复创建字符串对象。
  3. 字符串变量拼接的原理是StrngBuilder(1.8)
  4. 字符串常量拼接的原理是编译器优化 String s5="a"+"b";
  5. 可以使用intern方法,主动将串池中还没有的字符串对象放入串池
java 复制代码
 //串池中StringTable [ "a","b"]
        //堆中 [ "a", "b","ab" ] 这里注意串池中没有ab,因为这里是动态拼接的变量而不是常量。如果是String str="ab",则串池中存在。
        String s=new String("a")+new String("b");
        //想要把s的ab放入串池中调用 s.intern();即可。
        s.intern();
        String s2="ab";
        System.out.println(s==s2);
java 复制代码
        String x="ab";
        String s1=new String("a")+new String("b");
        String s2 = s1.intern();
        System.out.println(s2==x);//true
        System.out.println(s1==x);//false x的ab已经放进去了,实际上s1.intern()放不进去了,所以s1和x不相等

这里注意JDK及7以后:

java 复制代码
     String s1=new String("a")+new String("b");
        String s2 = s1.intern();
        String x="ab";
        System.out.println(s2==x);//true
        System.out.println(s1==x);//true 
java 复制代码
 String s1="a";
        String s2="b";
        String s3="a"+"b";
        String s4=s1+s2;
        String s5="ab";
        String s6=s4.intern();
        System.out.println(s3==s4);//false s3在常量池s4由StringBuilder拼接然后new String对象 在堆中
        System.out.println(s3==s5);//true  s3编译器优化 实际还是"ab"
        System.out.println(s3==s6);//true

        String x2=new String("c")+new String("d");
        String x1="cd";
        x2.intern();
        System.out.println(x1==x2);//false x2放不进去常量池因为已经存在了,所以x2.intern()返回的是常量池中的对象

5.StringTable的位置变更

5.1 为什么位置变换?

5.2 位置变更演示

java 复制代码
import java.util.ArrayList;
import java.util.List;

/**
 * 演示StringTable位置
 * -Xmx10m
 * -XX:-UseGCOverheadLimit [写+就是打开开关,-就是关闭。]
 *
 * -XX:-UseGCOverheadLimit 详解
 * 1. 作用
 * -XX:-UseGCOverheadLimit 是 JVM 的一个 故障保护机制开关,默认启用(-XX:+UseGCOverheadLimit)。
 * 它的核心作用是:
 * 当 JVM 检测到 GC 占用过多时间(超过 98%)但回收效果极差(释放内存 < 2%)时,抛出 OutOfMemoryError: GC Overhead Limit Exceeded 错误,防止应用陷入无限 GC。
 * 2. 触发条件
 * JVM 会在以下情况触发该错误:
 * GC 时间占比 > 98%(如 100ms 里 98ms 在 GC)。
 * GC 后内存释放 < 2%(几乎没回收空间)。
 * 持续超过 5 次 Full GC(不同 JVM 实现可能略有差异)。
 * 3. 关闭方式
 * 通过 -XX:-UseGCOverheadLimit 可禁用此机制,让 JVM 继续尝试 GC,而非直接报错。
 * 但需谨慎使用,可能让应用卡死在 GC 中!
 */
public class Demo {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int i = 0;
        try {
            for (int j = 0; j < 260000; j++) {
                list.add(String.valueOf(j).intern());
                i++;
            }
        } catch (Throwable e) {
            e.printStackTrace();
        } finally {
            System.out.println(i);
        }
    }
}

看报错也就知道了串池在堆空间。

6. StringTable垃圾回收

执行代码前的字符串常量池统计

往池子里加了100个对象后

java 复制代码
/**
 * 演示StringTable垃圾回收
 * -Xmx10m 堆空间设置10m
 * -XX:+PrintStringTableStatistics 打印字符串常量池的统计信息
 * -XX:+PrintGCDetails -verbose:gc 打印GC信息
 */
public class Demo {
    public static void main(String[] args) {
        int i=0;
        try {
            for (int j=0;j<100;j++){
                String.valueOf(j).intern();
                i++;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println( i);
        }


    }
}

后面改成10000个对象

java 复制代码
    public static void main(String[] args) {
        int i=0;
        try {
            for (int j=0;j<10000;j++){
                String.valueOf(j).intern();
                i++;
            }

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            System.out.println( i);
        }
    }

发生了GC

这里就证明了确实StringTable是会发生垃圾回收的。

7. StringTable性能调优

StringTable底层是哈希表。

这里是读48w个单词,-XX:StringTableSize=200000 【调整字符串常量池StringTable的大小】

这里就是动态的调整jvm参数发现执行的时间变化是很大的。

7.1 考虑字符串是否入池(字符串常量池)

for循环10次,将480w都存入list中。
没有入池之前 > 没有入池占用到80%左右。
入池之后

占用30%多左右。

如果引用出现大量的重复字符串,可以让字符串入池,来减少字符串对象个数,节约堆内存的使用。

相关推荐
chao_7891 小时前
二分查找篇——搜索旋转排序数组【LeetCode】一次二分查找
数据结构·python·算法·leetcode·二分查找
烛阴1 小时前
Python装饰器解除:如何让被装饰的函数重获自由?
前端·python
Boilermaker19921 小时前
【Java EE】Mybatis-Plus
java·开发语言·java-ee
aramae1 小时前
C++ -- STL -- vector
开发语言·c++·笔记·后端·visual studio
Tony小周1 小时前
实现一个点击输入框可以弹出的数字软键盘控件 qt 5.12
开发语言·数据库·qt
noravinsc2 小时前
django 一个表中包括id和parentid,如何通过parentid找到全部父爷id
python·django·sqlite
lixzest2 小时前
C++ Lambda 表达式详解
服务器·开发语言·c++·算法
ajassi20002 小时前
开源 python 应用 开发(三)python语法介绍
linux·python·开源·自动化
沉默媛2 小时前
如何安装python以及jupyter notebook
开发语言·python·jupyter
_Chipen3 小时前
C++基础问题
开发语言·c++