【Java 笔记】JVM : StringTable

JVM源码剖析之SymbolTable和StringTable 很多读者在观看JVM相关的书籍时会看到SymbolTable和StringTable,书中的三言二语介绍的不是很清楚,并且读者的水平有限,导致无法理解SymbolTable和StringTable。所以特意写此篇图文并茂的文章来彻底理解SymbolTable和StringTable这两张表。因为Hotspot是c++构成,所以也存在面向对象的思想,也即存在类和对象,所以直接看到SymbolTable和StringTable的类定义即可。 阅读详情

1.JVM更新:

在这里插入图片描述在这里插入图片描述
JDK1.8将String Table从方法区的常量池移至了堆中。

1.1 String Table移至Heap中的验证

List<String> list =new ArrayList<>();
try {
//不断往串池中添加字符串数据
    for (int j = 0; j < 200000; j++) {
        list.add(String.valueOf(j).intern());
    }
}catch (Throwable e){
    e.printStackTrace();
}
//-Xms10m -Xmx10m 

报错:java.lang.OutOfMemoryError: Java heap space

D:\Application\JDK13\bin\java.exe -Xms10m -Xmx10m "-javaagent:D:\Application\IDEAC\IntelliJ IDEA Community Edition 2019.3.4\lib\idea_rt.jar=63616:D:\Application\IDEAC\IntelliJ IDEA Community Edition 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath D:\Study\Javacode\out\production\Javacode JvmTest.test1
java.lang.OutOfMemoryError: Java heap space
 at java.base/java.util.Arrays.copyOf(Arrays.java:3721)
 at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
 at java.base/java.util.ArrayList.grow(ArrayList.java:235)
 at java.base/java.util.ArrayList.grow(ArrayList.java:242)
 at java.base/java.util.ArrayList.add(ArrayList.java:452)
 at java.base/java.util.ArrayList.add(ArrayList.java:465)
 at JvmTest.test1.main(test1.java:24)

2. String Table

常量池中的字符串仅仅是字面符号,只有当执行到对应的JVM指令时,才会生成对象,利用串池避免重复创建字符串对象,JDK1.8串池从常量池中移动到Heap中。String table是一个hashtable结构,所以不可能出现两个相同的值,其不可以扩容。

2.1 字节码解释

2.1.1 测试代码

public class StringTableTest {
    public static void main(String[] args) {
        String s1="a";
        String s2="b";
        String s3="ab";
        String s4=s1+s2;
        String s5="a"+"b";
        String s6=s4.intern();
        //判断输出结果?
        System.out.println(s3==s4);
        System.out.println(s3==s5);
        System.out.println(s3==s6);

    }
}

2.1.2 字节码文件

在IDEA的Terminal窗口输入命令: javap -verbose StringTableTest.class
得到字节码文件,截取其中的重要部分:

 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=7, args_size=1
         0: ldc           #7                  // String a
         2: astore_1
         3: ldc           #9                  // String b
         5: astore_2
         6: ldc           #11                 // String ab
         8: astore_3
         9: aload_1
        10: aload_2
        11: invokedynamic #13,  0             // InvokeDynamic #0:makeConcatWithC
onstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
        16: astore        4
        18: ldc           #11                 // String ab
        20: astore        5
        22: aload         4
        24: invokevirtual #17                 // Method java/lang/String.intern:(
)Ljava/lang/String;
        27: astore        6
        29: getstatic     #23                 // Field java/lang/System.out:Ljava
/io/PrintStream;
        32: aload_3
        33: aload         4
        35: if_acmpne     42
        38: iconst_1
        39: goto          43
        42: iconst_0
        43: invokevirtual #29                 // Method java/io/PrintStream.print
ln:(Z)V
        46: getstatic     #23                 // Field java/lang/System.out:Ljava
/io/PrintStream;
        49: aload_3
        50: aload         5
        52: if_acmpne     59
        55: iconst_1
        56: goto          60
        59: iconst_0
        60: invokevirtual #29                 // Method java/io/PrintStream.print
ln:(Z)V
        63: getstatic     #23                 // Field java/lang/System.out:Ljava
/io/PrintStream;
        66: aload_3
        67: aload         6
        69: if_acmpne     76
        72: iconst_1
        73: goto          77
        76: iconst_0
        77: invokevirtual #29                 // Method java/io/PrintStream.print
ln:(Z)V
        80: return
      LineNumberTable:
        line 7: 0
        line 8: 3
        line 9: 6
        line 10: 9
        line 11: 18
        line 12: 22
        line 14: 29
        line 15: 46
        line 16: 63
        line 18: 80
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      81     0  args   [Ljava/lang/String;
            3      78     1    s1   Ljava/lang/String;
            6      75     2    s2   Ljava/lang/String;
            9      72     3    s3   Ljava/lang/String;
           18      63     4    s4   Ljava/lang/String;
           22      59     5    s5   Ljava/lang/String;
           29      52     6    s6   Ljava/lang/String;

2.1.3 根据字节码文件理解JVM中StringTable的实现

public static void main(String[] args) {
    // 常量池中的信息,都会被加载到运行时常量池中, 这时 a b ab 都是常量池中的符号,还没有变为 java 字符串对象
    String s1="a";
   /* 0: ldc         #7     // String a
      2: astore_1
      // a 符号变为 "a" 字符串对象,并存入局部变量表中Slot=1,变量名为s1的位置
         以"a"作为键值去StringTable中做查询操作,StringTable中还未存有"a",
         于是将"a"存入StringTable中==》StringTable ["a"]
    */

    String s2="b";
    /* 3: ldc        #9     // String b
       5: astore_2
      // b 符号变为 "b" 字符串对象,并存入局部变量表中Slot=2,变量名为s2的位置
         以"b"作为键值去StringTable中做查询操作,StringTable中还未存有"b",
         于是将"b"存入StringTable中==》StringTable ["a","b"]

    */
    String s3="ab";
    /* 6: ldc        #11    // String ab
       8: astore_3
      // ab 符号变为 "ab" 字符串对象,并存入局部变量表中Slot=3,变量名为s3的位置
         同理 ==》StringTable ["a","b","ab"]
    */
    String s4=s1+s2;
   /* 9:  aload_1
      10: aload_2
      11: invokedynamic #13,  0             // InvokeDynamic #0:makeConcatWithC
      onstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
      16: astore   4
      //先加载变量aload_1 ("a" )和aload_2(" b "),再拼接
      //这一步实际等效于:new StringBuilder().append("a").append("b").toString()
      //此处是重新生成了一个新的对象
    */

    String s5="a"+"b";
    /*18: ldc        #11           // String ab
      20: astore     5
      //此处可以发现s5的生成并没有像变量s4一样先加载变量s1和s2
        而是和 String s3="ab" 对应的JVM指令一致,
        所以s5和s3指向同一对象
     */

    String s6=s4.intern();
    //调用intern()方法,先在String table中查找是否存在与该对象相同的字符串,
    // 若存在直接返回String table中字符串的引用,若不存在则将当前对象的引用放到String Table中,并且返回的是当前对象的引用
    //所以此处"ab"在StringTable中已存在,s6获得的是与s3相同的引用地址
    
    //运行结果:
    System.out.println(s3==s4);//false
    System.out.println(s3==s5);//true
    System.out.println(s3==s6);//true

}

2.1.4 String Table编译器调优

String s4=s1+s2;//StringBulider
String s5="a"+"b";//Javac 编译期直接确定

变量拼接是动态的,因为变量的指向在运行期是可以改变的,所以变量的拼接是通过new 一个新的对象实现的。
而常量是确定不变的,所以javac进行了优化,在编译期就确定了常量拼接的结果。

注意:常量的拼接结果会自动存入String Table,而变量的拼接结果需要使用String类的intern()方法存入String Table中。

2.2 Debug测试:对利用串池避免重复创建字符串对象的验证

public static void main(String[] args) {
    String s1="a";
    String s2="b";
    String s3="c";
    String s4="d";
    String s5="e";

    String s6="a";
    String s7="b";
    String s8="c";
    String s9="d";
    String s10="e";
}

首先设置三个断点,进行Debug测试,查看Memory窗口Java,lang.String的数量变化在这里插入图片描述
运行至断点1时,得到初始String的数量:
在这里插入图片描述
运行至断点2时,String如期望增加了5:
在这里插入图片描述
运行至断点3时,String的数量不变,说明没有重复生成"a",“b”,“c”,“d”,“e”:
在这里插入图片描述

2.3 String Table中存储的是String对象还是引用地址?

此部分对于网上查询到的资料有点蒙圈,个人总结如下:JDK1.8开始String Table中存储的既可以是String对象也可以是引用地址(如之后发现此结论有错,再做笔记更改)

2.3.1 Java中创建字符串对象的情况分析:

  1. 采用字面值的方式创建一个字符串时,JVM首先去串池中寻找是否存在该字符串,如果不存在,则在串池中创建该字符串,然后返回该字符串对象的引用地址;如果存在,则不创建任何新的对象,直接返回该字符串对象的引用地址。
  2. 采用new String(“abc”)方式创建字符串时,JVM首先去串池中寻找是否存在该字符串,如果不存在,则首先在串池中创建该字符串,然后再在堆中也创建一个该字符串对象,返回该字符串对象在堆中的引用地址,这种情况实际生成了两个String对象;如果存在,则只在堆中创建新的对象,返回该字符串对象在堆中的引用地址。

2.3.2 代码验证

Debug测试下观察Memory窗口中String数量的变化

断点1执行之后,String增量为1:在这里插入图片描述
断点2执行之后,较上一行代码String增量为2:
在这里插入图片描述

2.3.3 运行结果分析:

public static void main(String[] args) {
    String s1 = "a";
    String s2 = "b";
    String s3=new String("asd");//串池和堆中都会生成一个"asd"字符串对象,而s3指向的是堆中的字符串对象
    String s4=s3.intern();
    //"asd"在串池中已经存在,所以直接返回串池中该字符串对象的引用,而s3指向的是堆中的"asd"
    System.out.println(s4==s3);//false   该结果验证了以上分析

    String s5=s3+s1;//StringBuilder方式创建的拼接对象只在堆中存在
    String s6=s5.intern();//串池中不存在该字符串对象,此时存入的只是s5所指对象的引用地址,所以s6和s5指向的是同一地址
    System.out.println(s5==s6);//true   该结果验证了以上分析
}
医学图像配准实战:用Python实现MIND特征提取(附完整代码) 本文详细介绍了MIND(模态无关邻域描述符)在医学图像配准中的应用。通过Python和PyTorch实战,从原理到代码逐步解析了如何提取MIND特征,并利用其对结构而非灰度的感知能力,有效解决CT、MRI等多模态图像的对齐难题,为病灶追踪、手术导航等高级应用提供鲁棒性基础。 阅读详情

相关推荐

STM32F103C8T6+TB6612驱动直流电机全攻略(附PWM调速代码)

本文提供了一份基于STM32F103C8T6微控制器和TB6612驱动模块的直流电机驱动完整解决方案。详细解析了硬件连接、STM32CubeMX配置、HAL库驱动代码编写,并附带了PWM调速代码实现。内容涵盖从基础原理到实战调试的全过程,旨在帮助开发者快速构建稳定、可精准调速的电机控制系统,适用于智能车、机器人等嵌入式项目。

weixin_29181861的博客 586

Java --- JVM之StringTable

总结:在jdk6中,如果串池中有,就返回已有的串池中的对象的地址,如果没有,就是将这个对象复制一份,放入串池,并返回这个对象的地址。从jdk7起,如果串池中有,就返回已有的串池中的对象的地址,如果没有,就是将这个对象的引用地址复制一份,放入串池,并返回串池中的引用地址。③、在jdk6中的StringTable的是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。4、如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

qq_46093575的博客 439

Data+AI新玩法:Text2SQL让数据查询变得如此简单!

你是不是也遇到过这种情况?明明手头有海量数据,却因为SQL这道“门槛”,让分析和决策变得复杂和拖沓。你可能想着:“要是我能直接用自然语言和数据库对话,那该多好!你不是一个人在这样想,Text2SQL技术正是为了帮你摆脱这一困境而诞生的。

Ase5gqe的博客 865

Java字符串常量池详解(StringTable)

通过观察源码可知,String类的value是被private修饰的,类外是拿不到这个值的。 可能很多人以为String类不可变是因为value[] 这个数组被final修饰,但其实并不是这样的,因为value存储的是一个引用,而不是常量。

qq_63218110的博客 5613

IC版图设计之OASIS文件格式详解

OASIS(Open Artwork System Interchange Standard)是继GDSII之后新一代的集成电路版图数据交换标准,由SEMI在2004年正式发布(SEMI标准号:SEMI P39)。高压缩率:相比GDSII可减少3-10倍文件体积扩展性强:支持纳米级工艺的复杂图形描述跨平台兼容:独立于EDA工具和操作系统安全增强:内置校验和与加密支持。

ZZcppc的博客 2440

矩阵束(Matrix Pencil)入门:从通信系统到控制理论,它为何是建模利器?

本文深入探讨矩阵束(Matrix Pencil)在通信系统与控制理论中的关键作用,揭示其作为复杂系统建模利器的独特价值。从MIMO系统的多径干扰分析到电力系统稳定性判据,矩阵束通过`A-λB`方程求解提供精准建模方案,显著提升5G通信和工业控制等领域的性能表现。

weixin_30134145的博客 358

JVM中StringTable详解

String在JDK9中存储结构变更:官方网站说明:JEP 254: Compact Strings (java.net) 对官方中的内容说明进行翻译: 动机目前类的实现将字符存储在一个数组中,每个字符使用两个字节(16位)。从许多不同的应用中收集到的数据表明,字符串是堆使用的主要组成部分,此外,大多数字符串对象只包含Latin-1字符。这些字符只需要一个字节的存储空间,因此这些字符串对象的内部字符数组中有一半的空间没有被使用。 说明 我们建议将类的内部表示方法从字符数组改为字节数组加编码标志域。新的Str

写写平时的技术与感想 2323

Android AMS——AMS初始化(二)

Android AMS 也是一个系统服务,属于系统服务中的引导服务,在 startBootstrapServices() 方法中启动。这里我们就来看一下 ActivityManagerService 的启动流程。

小旭的专栏 778

JVM(十四)—— StringTable

String代表不可变的字符序列,简称不可变性。 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。 当调用String的replace方法修改指定字符或者字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。 通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

壹升茉莉清的博客 588

PLC为西门子的S7-1200,舵机为 DS3218,PLC控制舵机旋转程序实现?

🏆 本文收录于《全栈Bug调优(实战版)》专栏,致力于分享我在项目实战过程中遇到的各类Bug及其原因,并提供切实有效的解决方案。无论你是初学者还是经验丰富的开发者,本文将为你指引出一条更高效的Bug修复之路,助你早日登顶,迈向财富自由的梦想🚀!同时,欢迎大家关注、收藏、订阅本专栏,更多精彩内容正在持续更新中。让我们一起进步,Up!Up!Up!    备注: 部分问题/难题源自互联网,经过精心筛选和整理,结合数位十多年大厂实战经验资深大佬经验总结所得,数条可行方案供所需之人参考。

**My Coding Family** 998

AUTOSAR进阶图解==>AUTOSAR_SWS_ECUStateManager

AUTOSAR ECU State Manager 摘要 AUTOSAR ECU状态管理器(EcuM)是Classic Platform 4.4.0规范中的核心模块,负责管理ECU的整个生命周期,包括启动、运行、关机和睡眠等状态转换。EcuM通过协调各基础软件模块的初始化过程,确保系统可靠运行。 主要功能包括: 启动管理 - 控制从硬件初始化到应用运行的完整启动序列 状态转换 - 管理RUN/POST_RUN/SHUTDOWN等状态切换 唤醒验证 - 区分真实与虚假唤醒事件 关机序列 - 确保安全关机保存关

一名留德华的,康德的信徒的,专注于汽车电子领域的博客 2472

【JVM】【第十章】 StringTable

1.String的基本特性 String:字符串,使用一对""引起来表示。 1.1String的两种创建方式 String sl = "hello"; //字面量的定义方式; String s2 = new String("hello"); //new对象的方式 通过字面量的方式(不同于new)给一个String变量赋值,此时的字符串值声明在字符串常量池中。 1.2 String类的特点 String类是final的,不可被继承 String实现了Serializable接口,支持序列化 实现了C

weixin_43589563的博客 1230

OpenClaw实战系列03:Slack/Telegram国际版接入实战指南

在当今的软件开发和工作流自动化中,将项目管理工具与即时通讯平台集成已成为提高团队协作效率的关键。OpenClaw作为一个开源的自动化工具,通过接入Slack和Telegram国际版,可以实现实时通知、自动化任务触发和团队协作的无缝对接。本文将详细介绍如何实现OpenClaw与这两个主流即时通讯平台的集成。OpenClaw是一个开源的自动化工作流引擎,支持多种触发器和执行器,能够帮助开发团队实现CI/CD流程、任务调度、监控告警等自动化场景。模块化设计:支持插件化扩展多触发器支持。

PeterPan的博客 586

JVM系列——StringTable

1. String的基本特性: String就是字符串类型,定义方式有两种: String s1 = "hello"; String s2 = new String("hello"); String类是final修饰的,不可以被继承。 JDK1.9之后String、StringBuffer、StringBuilder从char[]改成byte[]了。 String代表不可变的字符序列,简称不可变性。 字面量定义的方式,存储在字符串常量池中。字符串常量池是不会存储相同内容的字符串的。Str.

m0_51660523的博客 2849

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

聊聊jvm的StringTable及SymbolTable

序 本文主要研究一下jvm的StringTable及SymbolTable StringTable及SymbolTable JDK的变动 在java7的时候将字符串常量池移到java heap,字符串常量池被限制在整个应用的堆内存中,在运行时调用String.intern()增加字符串常量不会使永久代OOM了。使用-XX:StringTableSize可以设置StringTableSize,默认...

weixin_34360651的博客 1330

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

对于StringTable的理解

String table又称为String pool,字符串常量池,其存在于堆中(jdk1.7之后改的)。最重要的一点,String table中存储的并不是String类型的对象,存储的而是指向String对象的索引,真实对象还是存储在堆中。 此外String table还存在一个hash表的特性,里面不存在相同的两个字符串。 此外String对象调用intern()方法时,会先在Strin...

gw_9527的博客 9352

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

JVM字符串常量池StringTable

介绍JVM字符串常量池,即String Table,包括基本特性和内存分配等。

m0_62946761的博客 4878

JVM中StringTable概述及调优

JVM中StringTable 三、特性 常量池中的字符串仅是符号,第一次用到时才变为对象 利用串池的机制,来避免重复创建字符串对象 字符串变量拼接的原理是StringBuilder(1.8) 字符串常量拼接的原理是编译器优化 可以使用intern方法,主动将串池中还没有的字符串对象放入串池 二、JVM编译期优化 StringTable为hashtable结构,不能扩容 public ...

陌尘吖的博客 1347

JVM学习笔记-----StringTable

本文详细解析了Java字符串常量池(StringTable)的运行机制和优化策略。首先介绍了字符串从符号到对象的转换过程,以及StringTable的哈希表结构特性。重点分析了不同JDK版本中字符串创建和intern()方法的差异:JDK7+会将堆中对象引用放入常量池,而JDK6会在永久代创建新对象。通过多个字符串比较示例,展示了编译期优化、动态拼接和intern方法的实际效果。最后提供了StringTable调优建议,包括调整哈希表大小、合理使用intern方法等,并指出大字符串和不常用字符串不适合入池。

2301_80860862的博客 1469

String及StringTable(二):java中的StringTable

文章目录1.什么是StringTable2.java中字符串拼接的秘密3.intern()方法与StringTable调优3.1 intern方法结束3.2 Stringtable参数调优4.String去重XX:+UseStringDeduplication5.若干有些坑的面试题5.1 不同方法创建String的内存模型 1.什么是StringTable 在前面部门已经涉及到了对StringTable的一些基本使用。但是或许很多人还并不知道什么是StringTable。StringTable也可称为Str

dhaibo1986的专栏 2419

图解JVM - 10.StringTable

图解 JVM 中关于 StringTable 的文章,介绍 String 基本特性(存储变革、核心特性)、内存分配机制(双模式)、基本操作原理(创建比较、方法影响)。

Anarkh_Lee的博客 1051

10.JVM-StringTable/StringPool

String的基本特性、String的内存分配、String的基本操作、字符串拼接操作、intern()的使用、new String("ab")会创建几个对象、new String("a") + new String("b") 会创建几个对象、G1中的String去重操作

Android系统优化 3451

JVM基础篇-StringTable

JVM基础篇-StringTable

冰点契约的博客 1242

JVM——StringTable

JVM——StringTable

qq_52025570的博客 1013

java string table_聊聊jvm的StringTable及SymbolTable

序本文主要研究一下jvm的StringTable及SymbolTableStringTable及SymbolTableJDK的变动在java7的时候将字符串常量池移到java heap,字符串常量池被限制在整个应用的堆内存中,在运行时调用String.intern()增加字符串常量不会使永久代OOM了。使用-XX:StringTableSize可以设置StringTableSize,默认是6553...

weixin_39851914的博客 402

JVM之StringTable

题目 public static void main(String[] args) { String str1 = "a"; String str2 = "b"; String str3 = "ab"; String str4 = "a" + "b"; String str5 = new String("ab"); String str6 = str5.intern(); String

weixin_40988088的博客 440
上一篇: 【Java 笔记 】直接内存
下一篇: 【Mybatis】基于注解的入门案例
JJ_Notebook
博客等级 码龄7年 1粉丝 42原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值