ry
ry
发布于 2023-08-14 / 81 阅读 / 0 评论 / 0 点赞

StringTable

综述

StringTable是一张HashTable,编译时生成,不可扩建。

StringTable哈希表[]-fmew.png

特性介绍

将常量池加载到运行时常量池中,采取懒加载方式,使用时 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()方法:将串池中没有的字符串对象加到其中

如果你的应用里有大量的字符串,而且这些字符串可能会存在重复的问题,那么可以让字符串入池来减少他的字符串对象个数,节约堆内存的使用。


评论