JVM

19-String Table

2021-05-27  本文已影响0人  紫荆秋雪_文

一、String的基本特性

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence

实例一:对字符串重新赋值

public class StringTableTest1 {
    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = "abc";
        s1 = "def";

        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}
false
def
abc
image.png
image.png

小结:s1和s2分别指向字符串常量池中不同的字符串,所以s1 != s2

实例二:对字符串进行连接操作

public class StringTableTest2 {
    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = "abc";
        s1 += "def";

        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}
false
abcdef
abc

小结

实例三:调用String的replace()方法修改指定字符串或字符时

public class StringTableTest3 {
    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = s1.replace('a', 'd');

        System.out.println(s1 == s2);
        System.out.println("dbc" == s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}
false
false
abc
dbc

二、String的内存分配

三、字符串拼接

1、常量与常量的拼接结果在常量池,编译期优化

public class StringTableTest5 {
    public static void main(String[] args) {

        String s1 = "abc";
        String s2 = "a" + "b" + "c";

        System.out.println(s1 == s2);
        System.out.println(s1);
        System.out.println(s2);
    }
}
image.png

2、常量池中不会存在相同内容的常量

3、在拼接过程中只要其中有一个是变量,结果就在堆中。变量拼接的原理是 StringBuilder

public class StringTableTest6 {
    public static void main(String[] args) {

        String s1 = "Hi-";
        String s2 = "JavaEE";

        String s3 = "Hi-JavaEE";
        String s4 = "Hi-" + "JavaEE";
        String s5 = s1 + "JavaEE";
        String s6 = "Hi-" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);

        System.out.println(s5 == s6);
        System.out.println(s5 == s7);

        System.out.println(s6 == s7);
        System.out.println(s6.equals(s7));
    }
}
true
false
false
false
false
false
false
true
image.png
  0 ldc #2 <Hi->    //  从常量池取出 "Hi-"
  2 astore_1        //  把 "Hi-"存储到 astore_1     —— s1
  3 ldc #3 <JavaEE>     //  从常量池取出 "JavaEE"
  5 astore_2            //  把 "JavaEE"存储到 astore_2      —— s2 
  6 ldc #4 <Hi-JavaEE>  //  从常量池取出 "Hi-JavaEE"
  8 astore_3            //  把 "JavaEE"存储到 astore_3      —— s3
  9 ldc #4 <Hi-JavaEE>  //  由于是字面量拼接,所以直接从常量池中获取 "Hi-JavaEE"
 11 astore 4        // 存储s4地址    
 13 new #5 <java/lang/StringBuilder>    //  字符串拼接时有变量存在时,首先创建 StringBuilder
 16 dup
 17 invokespecial #6 <java/lang/StringBuilder.<init>>   //  StringBuilder.init初始化
 20 aload_1     //  s1 地址
 21 invokevirtual #7 <java/lang/StringBuilder.append>   //  拼接
 24 ldc #3 <JavaEE>     //  从常量池取出 "JavaEE"
 26 invokevirtual #7 <java/lang/StringBuilder.append>   //  拼接
 29 invokevirtual #8 <java/lang/StringBuilder.toString> //  StringBuilder 转 String
 32 astore 5    // 存储s5
 34 new #5 <java/lang/StringBuilder>    //  创建 StringBuilder 对象
 37 dup
 38 invokespecial #6 <java/lang/StringBuilder.<init>>   // StringBuilder.init 初始化
 41 ldc #2 <Hi->   //  从常量池取出 "Hi-"
 43 invokevirtual #7 <java/lang/StringBuilder.append>   // 拼接
 46 aload_2     //  从aload_2中获取数据,也就是 "JavaEE"
 47 invokevirtual #7 <java/lang/StringBuilder.append>   //  拼接
 50 invokevirtual #8 <java/lang/StringBuilder.toString> // StringBuilder 转 String
 53 astore 6    //  存储s6
 55 new #5 <java/lang/StringBuilder>    //  创建 StringBuilder 对象
 58 dup
 59 invokespecial #6 <java/lang/StringBuilder.<init>>   //  StringBuilder.init 初始化
 62 aload_1     //  从aload_1中获取数据,也就是 "Hi-"
 63 invokevirtual #7 <java/lang/StringBuilder.append>   //  拼接
 66 aload_2     //  从aload_2中获取数据,也就是 "JavaEE"
 67 invokevirtual #7 <java/lang/StringBuilder.append>   //  拼接
 70 invokevirtual #8 <java/lang/StringBuilder.toString> // StringBuilder 转 String
 73 astore 7    // 存储s7
 75 getstatic #9 <java/lang/System.out>
public class StringTableTest6 {
    public static void main(String[] args) {

        final String s1 = "Hi-";
        final String s2 = "JavaEE";

        String s3 = "Hi-JavaEE";
        String s4 = "Hi-" + "JavaEE";
        String s5 = s1 + "JavaEE";
        String s6 = "Hi-" + s2;
        String s7 = s1 + s2;

        System.out.println(s3 == s4);
        System.out.println(s3 == s5);
        System.out.println(s3 == s6);
        System.out.println(s3 == s7);

        System.out.println(s5 == s6);
        System.out.println(s5 == s7);

        System.out.println(s6 == s7);
        System.out.println(s6.equals(s7));
    }
}
true
true
true
true
true
true
true
true
  0 ldc #2 <Hi->
  2 astore_1
  3 ldc #3 <JavaEE>
  5 astore_2
  6 ldc #4 <Hi-JavaEE>
  8 astore_3
  9 ldc #4 <Hi-JavaEE>
 11 astore 4
 13 ldc #4 <Hi-JavaEE>
 15 astore 5
 17 ldc #4 <Hi-JavaEE>
 19 astore 6
 21 ldc #4 <Hi-JavaEE>
 23 astore 7
 25 getstatic #5 <java/lang/System.out>

4、如果拼接的结果调用intern方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址

5、StringBuilder的效率


public class StringTableTest7 {
    public static void main(String[] args) {

//        plusStr(100000);    // 58380
        stringBuilderPlusStr(100000);   //  11
    }

    public static void plusStr(int count) {
        String s1 = "Raven";
        long start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            s1 += "_Raven_";
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }


    public static void stringBuilderPlusStr(int count) {
        StringBuilder s1 = new StringBuilder("Raven");
        long start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            s1.append("_Raven_");
        }
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}

四、intern()的使用

    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();

实例1

public class StringTableTest9 {

    public static void main(String[] args) {
        String s = new String("1");
        String s1 = s.intern();
        String s2 = "1";

        System.out.println(s == s1);
        System.out.println(s == s2);
        System.out.println(s1 == s2);
    }

}
false
false
true
0 new #2 <java/lang/String>     //  创建 String 对象s
3 dup
4 ldc #3 <1>        //  从字符串常量池中获取 1
6 invokespecial #4 <java/lang/String.<init>>    //String.init 初始化
9 astore_1      //  把 s 存储到 astore_1
10 aload_1      //  从 aload_1 获取数据
11 invokevirtual #5 <java/lang/String.intern>   // 调用String.intern
14 astore_2     //  把 s1 存储到 astore_2
15 ldc #3 <1>   //  从字符串常量池中获取 1
17 astore_3     //  把 s3 存储到 astore_3
18 getstatic #6 <java/lang/System.out>

分析

实例2

public class StringTableTest10 {
    public static void main(String[] args) {
        String s = new String("1") + new String("1");
        String s1 = s.intern();
        String s2 = "11";

        System.out.println(s == s1);
        System.out.println(s == s2);
        System.out.println(s1 == s2);
    }
}
true
true
true
0 new #2 <java/lang/StringBuilder>      //  创建 StringBuilder 对象
3 dup
4 invokespecial #3 <java/lang/StringBuilder.<init>>     //  StringBuilder.init 初始化
7 new #4 <java/lang/String>     //  创建 String 对象
10 dup
11 ldc #5 <1>       //  从字符串常量池中获取 1
13 invokespecial #6 <java/lang/String.<init>>       //  String.init 初始化
16 invokevirtual #7 <java/lang/StringBuilder.append>    //  StringBuilder对象拼接字符串 1
19 new #4 <java/lang/String>        //  创建 String 对象
22 dup
23 ldc #5 <1>       //  从字符串常量池中获取 1
25 invokespecial #6 <java/lang/String.<init>>       //  String.init 初始化
28 invokevirtual #7 <java/lang/StringBuilder.append>    //  StringBuilder对象拼接字符串 1
31 invokevirtual #8 <java/lang/StringBuilder.toString>  //  StringBuilder对象 转 String
34 astore_1
35 aload_1
36 invokevirtual #9 <java/lang/String.intern>

分析

小结:String的intern()使用

五、G1中String去重操作

背景:大多Java应用中

实现方式

命令行

UseStringDeduplication(bool);
PrintStringDeduplicationStatistics(bool);
StringDeduplicationAgeThreshold(uintx)
上一篇下一篇

猜你喜欢

热点阅读