Java 深入JVM分析String StringTable

文章目录

基本特性:

1、字符串常量池Jdk1.7之前位于方法区,1.7开始位于堆

2、字符串常量池中同样的数据只存储一份(固定大小HashTable存储数据)

3、使用 -XX:StringTableSize 可设置大小,不会像HashMap一样动态扩容,值太小造型Hash冲突严重,调用String.interns时性能会大幅下降

4、Jdk1.8中默认大小60013,1009是可设置最小值

字符串拼接:

1、通过StringBuilder的append()方法拼接字符串,自始至终只会创建一个StringBuilder的对象

2、使用String的字符串拼接,每次拼接都会创建一个StringBuilder和String对象,内存占用增大,也会增加GC频率

字符串拼接优化:

1、理论上初始化StringBuilder对象时,指定大小,从而设的数组大小,可以提高效率(减少扩容、复制次数)

2、但是,通过测试,发现设定大小与不设定,耗时相差无几(毫秒)

intern方法的使用:

情况一:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在,则会将当前字符串放入常量池中并把地址返回栈中引用,存在则将地址返回给栈中引用。

String s1 = “JavaEEHadoop”; //在字符串常量池中创建 “JavaEEHadoop”String s2 = new String(“JavaEEHadoop”).intern(); //会将字符串常量池中 “JavaEEHadoop” 地址 返回s2System.out.println(s1 == s2); true

情况二:如果对象在堆中已经创建了,字符串常量池中就不需要再创建新的对象了,而是直接保存堆中对象的引用,也就节省了一部分的内存空间

下述情况适用于Jdk1.7、1.8

/*** 此代码会在字符串常量池中 创建 “JavaEE”、”Hadoop“* 会使用StringBUilder来拼接,最后执行toString方法* * 此时在堆中是存在值为 “JavaEEHadoop” 的字符串对象的*/String s1 = new String(“JavaEE”) + new String(“Hadoop”);/*** 由于s1在堆中已存在,因此为了节省空间,字符串常量池中并不会创建 “JavaEEHadoop”* 而是保存 堆中对象的引用*/s1.intern();/*** 由于此刻字符串常量池中已存在,因此s2会指向常量池中 堆中对象的引用*/String s2 = “JavaEEHadoop”;System.out.println(s1 == s2); true

是否引用同一份对象?

String s1 = “JavaEE”;String s2 = “Hadoop”;String s3 = “JavaEEHadoop”;/*** 编译器优化 为 JavaEEHadoop,s3 == s4 为true,在字符串常量池中同一份*/String s4 = “JavaEE” + “Hadoop”;System.out.println(s3 == s4); true/*** 如果拼接符号的前后出现了变量* 1、StringBuilder.toString中 s = new StringBuilder();* 2、s.append(“JavaEE”);* 3、s.append(“Hadoop”);* 4、s.toString(); –>类似于 new String(char[])* 但跟String s = new String(“JavaEEHadoop”)不一样* 由于StringBuilder.toString中new String中参数是char[]数组* 因此并不会在字符串常量池中创建 ”JavaEEHadoop“*/String s1 = “JavaEE”; //字符串常量池中 “JavaEE”String s2 = “Hadoop”; //字符串常量池中 “Hadoop”String s3 = “JavaEEHadoop”;String s4 = s1 + “Hadoop”; String s5 = s1 + s2;System.out.println(s3 == s4); falseSystem.out.println(s3 == s5); false/*** final修饰的string变量相加时,编译器会优化为 ab,不会用StringBuilder拼接* s11 == s12 为true,在字符串常量池中同一份* s12会被编译器优化为 “ab”*/final String s1 = “a”;final String s2 = “b”;String s3 = “ab”;String s4 = s1 + s2;System.out.println(s2 == s4); true

创建了几个对象?

/*** 创建了1个或2个* 执行步骤,对应字节码步骤* * 1、堆中开辟String对象空间 new #8 * 2、如果 “ab” 在字符串常量池中存在,那么久不创建,如果不存在则创建* 3、初始化String对象*/String s1 = new String(“ab”);/*** 字节码*/步骤1、new #17 步骤1、dup步骤2、ldc #14 步骤3、invokespecial #18 /*** 如果字符串常量池中 “a”、”b”不存在,那么会创建6个对象* * 执行步骤对应字节码步骤* 1、堆中开辟StringBuilder对象空间,初始化StringBuilder对象 * 2、堆中开辟String对象空间* 3、在字符串常量池中创建 “a”* 4、初始化String对象* 5、执行append方法* * 6、堆中开辟String对象空间* 7、在字符串常量池中创建 “b”* 8、初始化String对象* 9、执行append方法* 10、执行toString方法* StringBuilder对象toString方法执行说明,由于返回的是String对象,因此会执行toString方法* 11、堆中开辟String对象空间* 由于调用的是new String(char[])构造方法* 因此并不会在字符串常量池中创建 “ab”*/String s1 = new String(“a”) + new String(“b”);/*** 字节码文件*/步骤1、new #8 步骤1、dup步骤1、invokespecial #9 步骤2、new #17 步骤2、dup步骤3、ldc #12 步骤4、invokespecial #18 步骤5、invokevirtual #10 步骤6、new #17 步骤6、dup步骤7、ldc #13 步骤8、invokespecial #18 步骤9、invokevirtual #10 步骤10、invokevirtual #11 步骤11、new #80 步骤11、dup//我们可以看到toString方法中并没有 在字符串常量池中创建 “ab”aload_0getfield #234 iconst_0aload_0getfield #233 invokespecial #291 areturn

提示:这里对文章进行总结:

建议大家不要卷、不要卷、不要卷

郑重声明:本文内容及图片均整理自互联网,不代表本站立场,版权归原作者所有,如有侵权请联系管理员(admin#wlmqw.com)删除。
上一篇 2022年6月16日 06:13
下一篇 2022年6月16日 06:13

相关推荐

联系我们

联系邮箱:admin#wlmqw.com
工作时间:周一至周五,10:30-18:30,节假日休息