微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM-StringTable

String的基本特性

  • String 字符串
    • String s1 = "vic"; //字面量的定义方式
    • String s2 = new String("vic");
  • String 声明为final,不可被继承
  • 实现了Serializable、Comparable,可序列化、可排序

JDK8及以前内部定义了final char[] values 存储字符串数据,JDK9之后改为byte[]

根据JDK官方描述:大部分内容都是拉丁文所以一个byte就能存储,所以改为byte[]+编码的方式节省空间

StringBuffer、StringBuild都同样的做了修改

不可变字符序列

  • 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原来的value进行赋值
  • 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
  • 调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原来的value赋值

通过字面量的方式给一个字符串赋值,此时的字符串值声明在字符串常量池中

字符串常量池中不会存在相同内容的字符串

String.intern() 如果字符串常量池中没有对应data字符串的话,则在常量池中生成,并反回在字符串常量池中地址

  • String的String Pool是一个固定大小的HashTable,JKD6及之前认长度为1009,JDK7后认长度调整为60013
    • 因为如果String Pool中放入过多的String,会造成比较多的Hash冲突,而导致链表长度变长,而链表长度变长后直接导致了String.intern性能下降
  • -XX:StringTableSize 可设置StringTable长度
    • JDK8时StringTableSize最小值1009

String的内存分配

String类型的常量池比较特殊,主要使用方式有两种:

  • 只用使用双引号声明出来的String对象会直接存储在常量池中。比如:String s = "String";
  • 如果不是用双引号声明的String对象,可以使用String提供的intern()方法

Java6及以前,字符串常量池存在永久代
Java7,字符串常量池的位置调整到Java堆内
Java8,字符串常量在堆内

为什么StringTable要调整?

  1. permSize认比较小
  2. 永久代垃圾回收的频率很低

String基本操作

public static void main(String[] args) {
    System.out.println();
    System.out.println("1");
    System.out.println("2");
    // 字符串"1"、"2"不会被再次加载
    System.out.println("1");
    System.out.println("2");
}
class Memory {
    public static void main(String[] args){
        int i = 1;
        Object obj = new Object();
        Memory mem = new Memory();
        mem.foo(obj);
    }

    private void foo(Object param) {
        String str = param.toString();
        System.out.println(str);
        
    }
}

image.png

字符串拼接操作

  1. 常量和常量的拼接结果在常量池,原理是编译期优化
  2. 只要其中一个是变量,结果就在堆中,变量拼接的原理是StringBuilder
  3. 如果拼接结果调用intern() 方法,则主动将常量池还没有的字符串对象放入池中,并返回次对象地址
public void test1() {
    String s1 = "a" + "b" + "c";
    String s2 = "abc";
    /*
     * 常量和常量的拼接结果在常量池
     */
    System.out.println(s1 == s2); // true
    System.out.println(s1.equal(s2)); // true
}
public void test2() {
    String s1 = "javaEE";
    String s2 = "hadoop";
    
    String s3 = "javaEEhadoop";
    String s4 = "javaEE" + "hadoop";
    String s5 = s1 + "hadoop";
    String s6 = "javaEE" + s2;
    String s7 = s1 + s2;
    
    /*
     * 只要其中一个是变量,则相当于在堆空间中new String(), 具体内容为拼接的结果:javaEEhadoop
     */
    System.out.println(s3 == s4); // true
    System.out.println(s3 == s5); // false
    System.out.println(s3 == s6); // false
    System.out.println(s3 == s7); // false
    System.out.println(s5 == s6); // false
    System.out.println(s5 == s7); // false
    System.out.println(s6 == s7); // false
    
    String s8 = s6.intern();
    System.out.println(s3 == s8); // true 
}
public void test3() {
    String s1 = "a";
    String s2 = "b";
    String s3 = "ab";
    /*
     * s1 + s2的执行细节:
     * 1. StringBuilder s = new StringBuilder();
     * 2. s.append("a");
     * 3. s.append("b");
     * 4. s.toString();   -> 约等于new String("ab");
     */
    String s4 = s1 + s2;
    System.out.println(s3 == s4); // false
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐