StringTable
运行时常量池
细节参考:https://www.cnblogs.com/54chensongxia/p/13708767.html
class文件 二进制字节码(类基本信息,常量池,类方法定义(包含了虚拟机指令))
常量池作用是给指令提供一些常量符号,根据常量符号以查表<符号表>方式查找
常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量(字符串,整数,boolean类型..)等信息
运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址 运行时常量池有StringTable,符号表(类字节码中的类名,方法名,变量名。。。)
StringTable
String s1 = "a";
String s2 = "b";
String s3="ab";
常量池中:常量,对象的引用
常量池最初存在于字节码文件,运行时加载到运行时常量池中,这时候都(仅)是常量池中的符号,还没变成java字符串对象,执行到ldc #2 会把符号变为 "a" 字符串对象,然后准备好一片空间StringTable[] (刚开始是空的,数据结构是hash表),没找到"a",就把"a"字符串对象放入串池塘,"b"同理。用到这行代码才会开始创建这个字符串对象(ldc(加载) #2)(懒惰行为)
"ab"类似
String s1 = "a";
String s2 = "b";
String s3="ab";
String s4 = s1 + s2;
new StringBuilder().append("a").append("b").toString()
toString()方法又new String(new StringBuilder().append("a").append("b"))=new String("ab") jdk1.8
System.out.println(s3==s4); false
s3在串池 s4在堆中
String s1 = "a";
String s2 = "b";
String s3="ab"; "ab" 执行到这还没有被创建,会创建
String s4 = s1 + s2;
String s4="a"+"b";在常量池中直接找到的已有"ab" javac在编译器的一个优化,编译期间结果已经确定为ab(都是常量)了,
String s4 = s1 + s2; s1 s2是变量,在运行只可能被修改,结果不能确定,必须在运行期间用 StringBuilder 动态拼接
System.out.println("xxx");也会创建字符串对象放入串池,他是执行到这行才会创建。(字符串延迟加载)
intern
可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
//["a","b"]
String s = new String("a") + new String("b");
//堆中 new String("a") new String("b") new String("ab")
String s2 = s.intern(); //把s这个字符串<对象>尝试放入串池,如果有并不会放入,如果没有则放入,返回串池中的对象,这里是成功
System.out.println(s2 == "ab");//true
System.out.println(s == "ab");//true
=================
String x = "ab"; //["ab"]
String s = new String("a") + new String("b");
//堆中 new String("a") new String("b") new String("ab")//["ab","a","b"]
String s2 = s.intern(); //把s这个字符串<对象>尝试放入串池,如果有并不会放入,如果没有则放入,返回串池中的对象,这里是失败
System.out.println(s2 == "ab");//true s2串池中
System.out.println(s == "ab");//true s堆
===========================================================================================
1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,会把串池中的对象返回 放入串池的对象与调用intern的对象是两个对象。 副本和堆中不是一个
String x = "ab"; //["ab"]
String s = new String("a") + new String("b");
//堆中 new String("a") new String("b") new String("ab")//["a","b","ab"]
String s2 = s.intern(); //把s这个字符串<对象>尝试放入串池,如果有并不会放入,如果没有则拷贝一份放入,返回串池中的对象,这里是失败 ["a","b","ab"]
System.out.println(s2 == x);//true s2串池的 x串池的
System.out.println(s == x);//false s堆中的
=================
String s = new String("a") + new String("b"); //["a","b"]
//堆中 new String("a") new String("b") new String("ab")
String s2 = s.intern(); //把s这个字符串<对象>尝试放入串池,如果有并不会放入,如果没有则拷贝一份放入,返回串池中的对象,这里是成功 ["a","b","ab"]
String x = "ab";
System.out.println(s2 == x);//true s2串池的 "ab串池的"
System.out.println(s == x);//false s堆中的
StringTable的位置
1.7从永久代到堆中,永久代回收效率低(Full GC才会回收),间接导致StringTable回收效率低,容易导致永久代内存不足。
堆中Minor GC就会回收。
往常量池灌字符串常量并且加入list,防止垃圾回收
1.6 OOM(PermGen space)
1.7 OOM(GC overhead limit exceede) 98%时间 2%被回收 关闭这个选项就是OOM(java heap space)
StringTable垃圾回收机制
底层类似hash表,数组(桶)+链表 会垃圾回收
StringTable性能调优
数组(桶)越多,冲突小,查找速度越快,入池时间越短。调优就是调整桶的个数至少1009 -XX:StringTableSize=xx
考虑将字符串对象是否入池 (相同字符串,不会入池)去重