String.intern in Java 6, 7 and 8 – string pooling
通过String.intern()
,值相同的字符串们在JVM里可以只占用一个String对象,从而减少内存的消耗。
另外程序使用过的String literal和String类型的常量也会被intern。
“intern”在这里是“拘留”的意思,
n. 实习生,实习医师
vt. 拘留,软禁
vi. 作实习医师
String.intern("foo")
就是把字符串“foo”“拘留”在String类内部的一个pool里。
String pool实际上是一个固定大小的哈希表。 在Java 7之后,String pool占用的是堆(heap)内存。
因为哈希表是固定大小的,不会自动扩容,所以大小的设置很重要。 如果相比于要intern的字符串数目,哈希表的大小设置得过小的话,哈希表的性能就可能退化成线性搜索。
# 在已经intern了1000000个字符串的情况下,再intern 10000个字符串
# pool的大小是60013,会有很多collision,哈希表性能退化
time = 1.913 sec
# pool的大小是100003
time = 0.012 sec
和普通的HashMap不同的是,String pool里的字符串如果没有地方引用,可以被垃圾回收。
从这个方面看,String pool的行为和WeakHashMap<String, WeakReference<String>>
差不多。
但是WeakHashMap<String, WeakReference<String>>
消耗的内存是String pool的实现的5倍。
Java7u40之后String pool的默认大小是60013。
可以通过-XX:StringTableSize=N
选项,给pool指定合适的大小。
考虑到哈希表的性能,pool的大小最好是质数,比如60013。
同时为了避免冲突(collision),pool的大小可以设成比“要intern的字符串数目X2”大一点的质数。
Java 6的时代,String pool放在PermGen区,所以intern太多字符串会导致PermGen区out of memory。
另外,选项-XX:+PrintStringTableStatistics
可以打印出String pool的使用情况。
选项-XX:+PrintFlagsFinal
可以显示String pool的大小。