【JVM之内存与垃圾回收篇】StringTable (3)

注意,我们左右两边如果是变量的话,就是需要 new StringBuilder 进行拼接,但是如果使用的是 final 修饰,则是从常量池中获取。所以说拼接符号左右两边都是字符串常量或常量引用 则仍然使用编译器优化。也就是说被 final 修饰的变量,将会变成常量,类和方法将不能被继承。

在开发中,能够使用 final 的时候,建议使用上

public static void test4() { final String s1 = "a"; final String s2 = "b"; String s3 = "ab"; String s4 = s1 + s2; System.out.println(s3 == s4); }

运行结果

true 拼接操作和append性能对比 public static void method1(int highLevel) { String src = ""; for (int i = 0; i < highLevel; i++) { src += "a"; // 每次循环都会创建一个StringBuilder对象 } } public static void method2(int highLevel) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < highLevel; i++) { sb.append("a"); } }

方法 1 耗费的时间:4005ms,方法 2 消耗时间:7ms

结论:

通过 StringBuilder 的 append() 方式添加字符串的效率,要远远高于 String 的字符串拼接方法

好处

StringBuilder 的 append 的方式,自始至终只创建一个 StringBuilder 的对象

对于字符串拼接的方式,还需要创建很多 StringBuilder 对象和调用 toString 时候创建的 String 对象

内存中由于创建了较多的 StringBuilder 和 String 对象,内存占用过大,如果进行 GC 那么将会耗费更多的时间

改进的空间

我们使用的是 StringBuilder 的空参构造器,默认的字符串容量是 16,然后将原来的字符串拷贝到新的字符串中,我们也可以默认初始化更大的长度,减少扩容的次数

因此在实际开发中,我们能够确定,前前后后需要添加的字符串不高于某个限定值,那么建议使用构造器创建一个阈值的长度

intern()的使用

intern 是一个 native 方法,调用的是底层 C 的方法

字符串池最初是空的,由 String 类私有地维护。在调用 intern 方法时,如果池中已经包含了由 equals(object) 方法确定的与该字符串对象相等的字符串,则返回池中的字符串。否则,该字符串对象将被添加到池中,并返回对该字符串对象的引用。

如果不是用双引号声明的 string 对象,可以使用 string 提供的 intern 方法:intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
比如:

String myInfo = new string("I love atguigu").intern();

也就是说,如果在任意字符串上调用 string.intern 方法,那么其返回结果所指向的那个类实例,必须和直接以常量形式出现的字符串实例完全相同。因此,下列表达式的值必定是 true:

("a"+"b"+"c").intern()=="abc"

通俗点讲,Interned string 就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,加快字符串操作任务的执行速度。注意,这个值会被存放在字符串内部池(String Intern Pool)

intern的空间效率测试

我们通过测试一下,使用了 intern 和不使用的时候,其实相差还挺多的

/** * 使用Intern() 测试执行效率 * @author: Nemo */ public class StringIntern2 { static final int MAX_COUNT = 1000 * 10000; static final String[] arr = new String[MAX_COUNT]; public static void main(String[] args) { Integer [] data = new Integer[]{1,2,3,4,5,6,7,8,9,10}; long start = System.currentTimeMillis(); for (int i = 0; i < MAX_COUNT; i++) { arr[i] = new String(String.valueOf(data[i%data.length])).intern(); } long end = System.currentTimeMillis(); System.out.println("花费的时间为:" + (end - start)); try { Thread.sleep(1000000); } catch (Exception e) { e.getStackTrace(); } } }

结论:对于程序中大量使用存在的字符串时,尤其存在很多已经重复的字符串时,使用 intern() 方法能够节省内存空间。

大的网站平台,需要内存中存储大量的字符串。比如社交网站,很多人都存储:北京市、海淀区等信息。这时候如果字符串都调用 intern() 方法,就会很明显降低内存的大小。

面试题 new String("ab")会创建几个对象 /** * new String("ab") 会创建几个对象? 看字节码就知道是2个对象 * * @author: Nemo */ public class StringNewTest { public static void main(String[] args) { String str = new String("ab"); } }

我们转换成字节码来查看

0 new #2 <java/lang/String> 3 dup 4 ldc #3 <ab> 6 invokespecial #4 <java/lang/String.<init>> 9 astore_1 10 return

这里面就是两个对象

一个对象是:new 关键字在堆空间中创建

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zgjgfw.html