• JVM——3.StringTable字符串常量池


    这篇文章我们来讲一下StringTable字符串常量池

    目录

    1.引例

    2.StringTable的特性

    3.StringTable的位置

    4.StringTable的垃圾回收、

    5.StringTable性能调优

    6.总结

    1.引例

    首先,我们来看下面的这段程序,请思考最终的输出结果。

    输出结果:

    解释:

    首先,我们的程序会被JDK中的编译器编译成java的二进制字节码文件,然后通过类加载器将其加载到JVM的内存的栈中,其中会生成一个常量池(就是一张常量池表),里面放在这个类编译后的各种字面量和符号注意,此时常量池中只存储了这些字面量的符号,没有生成具体对象。举个例子来说,就比如第7行的a,它在常量池中可能就是用一个符号25来表示的,没有具体的String类型的变量a,也不会开辟新的空间来存储a。然后根据程序计数器来一步一步的运行该程序。当执行到这一行,常量池中的信息会被加载到运行时常量池中,常量池中原本记录的符号也会变为真实的地址,即符号25变为地址25,并且会在堆中开辟一块空间存储String类型的变量a,这块开辟的空间就称为串池(即字符串常量池,即StringTable),它在堆中,其中里面一开始时是空的,当运行到这一行时,会把a放入里面,a的地址为25(假设的)。这就是整体的流程。然后就是依次在串池中放入b和ab。当执行到第10行时,它创建的是一个StringBuild对象,放入s1的值即a,然后调用StringBuilder的方法,进行字符串拼接。然后再创建一个新的String对象,里面放的就是拼接后的结果,即ab,很明显,s4中的ab是在堆中的,而"=="符号判断的是两个对象的地址是否相等,所以很明显,第14行输出false。当程序运行到第11行时,jvm会先在串池中找是否有ab,找到了,那么就不再创建新的对象了,就直接把这个ab的地址赋予s5,所以第15行输出true。下面再看第9行,这种字符常量相加的是直接相加的,没有创建对象调用方法。所以最后相加的结果就直接放在运行时常量池的串池中,所以第14行输出false,第15行输出true。对比着看第9行和第10行,第9行javac在编译时进行了优化,因为第9行是两个字符常量相加的,最终的结果是一定的,不会变的,所以就直接加,然后放到串池中。而第10行是因为这是两个变量相加,不确定最后的结果,所以就使用了StringBuilder类对象来进行操作。多说一句,字符串还具有延迟实例化的特点,具体来说就是在编译结束后不会直接实例化该字符串,直到代码运行到这一句的时候才会实例化出具体的字符串,并放入内存中。

    下面再来看一下第12行,第12行调用了intern方法。这个方法的作用是主动将串池中还没有的字符串对象放入串池。第12行是s4调用了intern方法的,我们知道,s4的引用是指向堆内存中的ab的,s4调用这个方法,jvm会先看运行时常量池中有没有ab这个字符对象,如果没有,那么就把堆中的ab放入到运行时常量池中,注意,放入后,堆中就没有这个ab了;如果jvm发现运行时常量池中有这个ab对象,那么就直接把s6的引用指向它。这就是intern的作用。

    这里关联一下字符串的不可变性,字符串的不可变性是一个引用多个对象,而这里将的是多个引用一个"对象"(不是一个对象,仅仅是值相等而已)

    2.StringTable的特性

    下面看一下StringTable的特性:

    1. 常量池中的字符串仅是符号,第一次用到时才变为对象
    2. 利用串池的机制,可以避免重复创建字符串对象
    3. 字符串变量拼接的原理是StringBuilder (1.8)
    4. 字符串常量拼接的原理是编译期优化
    5. 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

    注意:在jdk1.8以后,intern方法是把堆中对象的值挪到串池中,1.8以前,是将堆中的值复制一份,然后放到串池中。这一点需要注意。


    3.StringTable的位置

    下面来看一下StringTable的位置,如下图所示:

    如图所示,在jdk1.8以后,StringTable就在堆中了。StringTable就是字符串常量池!

    4.StringTable的垃圾回收

    这里我们只需要了解StringTable是可以进行垃圾回收的,具体是怎么回收的,我们后面将GC的时候会具体的讲,这里就不多说了。

    5.StringTable性能调优

    StringTable的底层的哈希表,StringTable进行性能调优就是要调整哈希表。

    性能调优的方法:

    • 调整 -XX:StringTableSize=桶个数
    • 考虑将字符串对象是否入池

    6.总结

    这篇文章我们主要讲解了StringTable。下面总结一下。

    StringTable,即字符串常量池,是存储字符串对象用的,在堆中。一个字符串,在被编译后,是不会创建对象的,仅仅只会在常量池中存储一个符号,只有当运行到这行代码的时候,才会在字符串常量池中创建对象,这就是字符串的懒加载,也是延迟加载。然后在字符串常量池中,也可以避免创建重复的对象。即如果StringTable中已经有了该对象,那么如果再有引用需要创建相同值的对象的时候,该引用会直接指向这个对象,就避免了重复创建。StringTable还可以进行垃圾回收。还讲述了StringTable的性能调优的两点策略。这就是本篇文章的所有内容。


     

  • 相关阅读:
    计算机专业哀鸿遍野:低代码平台和程序员水火不容,马上被取代
    交换机和路由器技术-35-NAT转PAT
    Python分支结构和循环结构
    您的captcha验证码设置对了吗?
    边读边递归
    Matlab | TCP通信
    Linux——ansible剧本
    【深度学习CPU(番外篇)——初识总线】
    Kotlin Files readAllBytes readAllLines readString
    毕业设计选题之Java+springboot线上蔬菜销售与配送系统(源码+调试+开题+lw)
  • 原文地址:https://blog.csdn.net/m0_52096593/article/details/132910618