StringTable详解

一、存储结构

jdk8及以前String底层使用char[],一个char是两个字节,jdk9开始改用byte[]加上编码标记节约空间。

jdk9官网提供的String修改说明:http://openjdk.java.net/jeps/254

修改动机:

二、不可变性

1、当对字符串重新赋值时,需要重写指定内存区域赋值

2、当对现有字符串进行连接操作时,也需要重新指定内存区域。

3、调用String的replace方法时修改指定字符串或者字符时,也需要重新指定内存区域赋值。

4、通过字面量方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

三、字符串常量池是不会存储相同内容的字符串的。

1、String的String Pool是一个固定大小的HashTable,默认值大小长度是1009,如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是调用String.intern时性能会大幅下降。

2、-XX:StringTableSize可设置StringTable的长度。

3、在jdk6中StringTable是固定的,就是1009的长度,如果常量池中的字符串过多就会导致效率下降很快。

4、在jdk7中,StringTable的长度默认是60013,jdk8开始StringTable的1009是可设置的最小值,默认值是60013。

四、String的内存分配

jdk6及以前,字符串常量池存放在永久代。jdk7开始字符串常量池的位置调整到java堆中。

jdk8去除了永久代,开始使用元空间,字符串常量池存放于堆中。

五、字符串优化

1、常量和常量的拼接结果在常量池中,直接使用常量池的字面量,原理是编译器优化。

2、字符串拼接中,出现常量和字符串变量的拼接,那个字符串变量相当于在堆空间中new StringBuilder(),具体的内容为拼接的结果。

3、str.inter(),判断字符串常量池中是否存在str,如果存在,则返回常量池中str的地址,如果不存在则在常量池中加载一份str,同时返回str的地址。

例:观察s4=s1+s2

s4的编译执行过程

(1)生成StringBuilder()对象,在jdk5.0之前使用StringBuffer()对象

(2)调用StringBuilder对象的append方法加入常量池中"a"

(3)调用StringBuilder对象的append方法加入常量池中"b"

(4)调用StringBuilder对象的toString方法

注意:如果变量s1和s2前加入了final修饰符,此时编译器会在编译阶段做优化,不会生成StringBuilder对象,直接形成从常量池中找"ab"返回给s4,当然s3==s4也是true。

六、intern

str.inter(),判断字符串常量池中是否存在str,如果存在,则返回常量池中str的地址,如果不存在则在常量池中加载一份str,同时返回str的地址。

1、jdk1.6,将字符串str尝试放入常量池

(1)如果常量池中有,则并不会放入,返回已有常量池中的对象的地址

(2)如果没有,会把此对象复制一份,放入常量池,并返回常量池的对象地址。

2、jdk1.7及以后版本,将字符串str尝试放入常量池

(1)如果常量池中有,则不会放入,返回已有的常量池中的对象地址

(2)如果没有,则把对象的引用地址复制一份,放入常量池中,并返回常量池中引用的地址。

七、扩展知识

1、new String("ab");会产生几个对象

一共有2个对象,在第一个堆中new String生成,第二个是堆中常量池中的ab对象

2、new String("a")+new String("b");会产生多少个对象

一共会产生6个对象

(1)有拼接操作,会生成StringBuilder对象

(2)new String("a")对象

(3)常量池中的"a"对象

(4)new String("b")对象

(5)常量池中"b"对象

(6)拼接完后会调用StringBuilder对象的toString()方法,toString方法内会new String()对象

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

3、intern在jdk版本中的区别

public static void main(String[] args) {
   String s=new String("1");
   s.intern();//调用intern()前,字符串常量池中已经存在"1"了。
   String s2="1";
   System.out.println(s==s2);//jdk6:false,jdk7/8:false
   String s3=new String("1")+new String("1");
   s3.intern();
   String s4="11";
   System.out.println(s3==s4);//jdk6:false,jdk7/8:true
}

jdk6:

jdk7/8:

注意:s4和s3.intern()互换

String s3=new String("1")+new String("1");
String s4="11";
s3.intern();
System.out.println(s3==s4);//jdk6:false,jdk7/8:false

八、G1对String去重(默认是不开启的)

java工程师测试了大部分java应用出现如下现象

1、堆中存活的String对象占25%

2、堆中存活的重复的String对象有13.5%

3、String对象的平均长度是45

堆上存在重复的String对象势必造成内存的浪费,G1垃圾回收器会在自动持续的去重String对象,避免堆内存浪费。

G1去重的实现步骤:

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值