综述
StringTable是一张HashTable,编译时生成,不可扩建。
特性介绍
将常量池加载到运行时常量池中,采取懒加载方式,使用时 ldc #常量池编号加载对应字符串,将常量池中的符号转换为java字符串对象。
串池:StringTable["a","b","ab"],HashTable结构,不可扩容。
下面编写一个简单的java文件
//StringTable["a","b","ab"] HashTable 结构,不能扩容
public class Demo{
//常量池中的信息,会被加载到运行时常量池中,这时,a b ab 都是常量池中的符号,还没有变成java字符串对象。
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s3 = "ab";
String s4 = s1 + s2;
/**
* new StringBuilder().append("a").append("b").toString()
* --> new String("ab")
* s3在串池中,s4在堆内存中
**/
System.out.println(s3 = s4);
String s5 = "a" + "b";
/**
* javac在编译期间的优化,结果已经在编译期间确定为"ab"
**/
System.out.println(s4 = s5);
}
}
$ javac Demo
$ javap -v Demo.class
执行命令后查看Demo.class反编译后的常量池信息
Constant pool:
#1 = Methodref #2.#3 // java/lang/Object."<init>":()V
#2 = Class #4 // java/lang/Object
#3 = NameAndType #5:#6 // "<init>":()V
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = String #8 // a
#8 = Utf8 a
#9 = String #10 // b
#10 = Utf8 b
#11 = String #12 // ab
#12 = Utf8 ab
#13 = InvokeDynamic #0:#14 // #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#14 = NameAndType #15:#16 // makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#15 = Utf8 makeConcatWithConstants
#16 = Utf8 (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
#17 = Class #18 // Demo
#18 = Utf8 Demo
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 SourceFile
#24 = Utf8 Demo.java
#25 = Utf8 BootstrapMethods
#26 = MethodHandle 6:#27 // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#27 = Methodref #28.#29 // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#28 = Class #30 // java/lang/invoke/StringConcatFactory
#29 = NameAndType #15:#31 // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#30 = Utf8 java/lang/invoke/StringConcatFactory
#31 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
#32 = String #33 // \u0001\u0001
#33 = Utf8 \u0001\u0001
#34 = Utf8 InnerClasses
#35 = Class #36 // java/lang/invoke/MethodHandles$Lookup
#36 = Utf8 java/lang/invoke/MethodHandles$Lookup
#37 = Class #38 // java/lang/invoke/MethodHandles
#38 = Utf8 java/lang/invoke/MethodHandles
#39 = Utf8 Lookup
{
public Demo();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 2: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=6, 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:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
16: astore 4
18: ldc #11 // String ab
20: astore 5
22: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 9: 9
line 16: 18
line 24: 22
}
存储位置
jdk1.6中存储位置为方法区中的常量池
jdk1.8中改放为堆中
垃圾回收
stringtable也会受到垃圾回收的管理,垃圾回收只会在内存紧张的时候当内存空间不足时,stringtable中那些没有被引用的字符串常量,仍然会被垃圾回收。
stringtable底层的实现类似于hashtable的实现,即他是哈希表,哈希表是数组加链表的结构,每个数组的元素称之为桶
性能调优
桶 -XX:StringTableSize=1099(桶个数)
stringtable的底层是哈希表,哈希表的性能是跟它的大小密切相关的。如果哈希表的桶的个数比较多,那么相对这个元素就比较分散,那么哈希碰撞的几率就会减少,查找的速度也会变快。反之,如果桶的个数比较少,那么哈希碰撞的几率就会增高,从而查找的速度也会受到影响。调优其实就是调桶的个数。
.intern()方法:将串池中没有的字符串对象加到其中
如果你的应用里有大量的字符串,而且这些字符串可能会存在重复的问题,那么可以让字符串入池来减少他的字符串对象个数,节约堆内存的使用。