java语义中的常量池有三个概念:
1. 一个是class文件中的常量池,这里存放着类、字段和方法的名称,静态成员变量的值,字符串常量等一系列类相关的信息:
Constant pool:
#1 = Class #2 // com/learn/clazz/file/SampleClass
#2 = Utf8 com/learn/clazz/file/SampleClass
#3 = Class #4 // java/util/ArrayList
#4 = Utf8 java/util/ArrayList
#5 = Class #6 // java/lang/Cloneable
#6 = Utf8 java/lang/Cloneable
#7 = Class #8 // java/io/Serializable
#8 = Utf8 java/io/Serializable
#9 = Utf8 serialVersionUID
#10 = Utf8 J
...
2. 另一个是JVM执行过程中的运行时常量池,运行时常量池是每个class或interface的class文件常量池在内存中的映射,类加载时被创建。每个运行时常量池所使用的内存区域都在JVM方法区中分配,JVM规范中说明,方法区逻辑上是堆的一部分,但无需像堆一样被gc,JVM并没有规定具体的方法区实现。hostspot实现中,方法区在jdk7和jdk8的实现中有较大变动,详情见 JVM运行时内存区域 方法区介绍。
3. 还有一个是字符串常量池,这个概念没有在JVM Specification中找到,Specification只说字符串常量存放在运行时常量池,对于如何组织字符串常量并没有约定。hotspot实现中和字符串常量池相关的概念是StringTable,在JVM启动参数中增加-XX:+PrintStringTableStatistics,能在程序执行结束后输出应用的常量池使用情况(只能在程序终止的时候,正常或异常):
SymbolTable statistics:
Number of buckets : 20011 = 160088 bytes, avg 8.000
Number of entries : 11609 = 278616 bytes, avg 24.000
Number of literals : 11609 = 510272 bytes, avg 43.955
Total footprint : = 948976 bytes
Average bucket size : 0.580
Variance of bucket size : 0.581
Std. dev. of bucket size: 0.762
Maximum bucket size : 6
StringTable statistics:
Number of buckets : 60013 = 480104 bytes, avg 8.000
Number of entries : 1583 = 37992 bytes, avg 24.000
Number of literals : 1583 = 145688 bytes, avg 92.033
Total footprint : = 663784 bytes
Average bucket size : 0.026
Variance of bucket size : 0.027
Std. dev. of bucket size: 0.163
Maximum bucket size : 2
StringTable使用HashTable的数据结构存储,Number of buckets : 60013意味着table的长度是60013,这是缺省的值,可以使用 -XX:StringTableSize=10000 改变。单个bucket 8bytes意味着是64位系统。Number of entries表示hashTable中元素个数,Number of literals表示字符串常量的个数,Maximum bucket size : 2 表示当前单个槽中最多存放两个entity,该指标决定了hash碰撞的程度(冲突过多影响gc时遍历StringTable做数据回收的效率,要增加buckets size,网上有看到增加buckets size提升ygc效率)。
java代码中的字符串常量引用都记录在StringTable里,从jdk7开始,字符串对象开始记录在堆,因此StringTable过大会造成oom。java代码中的字符串常量和用户通过String.intern()方式操作的字符串会进入StringTable。在堆内存不够用时,StringTable中引用的字符串常量也可以被gc回收。
jdk7版本里,字符串常量被移到了堆中,符号表移到native memory。
字符串常量池存在的意义是相同字符串复用,减少内存开销。