作者 | 浩说编程
来源 | 公众号:浩说编程
[ 大厂技术资源 | 研发必备安装包 | 限时免费获取 ]
String在日常开发中的使用频率应该不需要我过多形容,大家闭着眼睛都能手写出来,但也正因如此,对于String的性能优化最容易被忽视却也最为必要!
如何优化String的内存开支?
怎样用更少的空间管理String?
让我们带着这几个问题开始本篇的探讨。
一、String自身的“优化”设计思想
“String 和 StringBuffer 有什么区别?”
“StringBuffer可变,String不可变。”
“String为什么不可变?”
“额…”
这是一段面试的真实案例,面试者直接被pass掉,这也反映了一个普遍的问题:“知其然而不知其所以然”,希望大家引以为鉴,下面就来探讨一下“String为什么不可变?”
先从一个例子开始:
String str_1 = "abc";
String str_2 = "abc";
System.out.print(str_1==str_2);//true
123
相信大家都知道答案,重点来拆解一下每一步的底层逻辑:
1、String str_1 = “abc”;
首先,在常量池中查询"abc"是否已存在:
发现不存在,于是将"abc"初始化到常量池中,str_1引用该内容:
2、String str_2 = “abc”;
再次创建相同字符串"abc",同样在常量池中查询"abc"是否已存在
判断已经存在,于是不再重复创建内容,使用相同的引用:
于是得出答案true。
那么试想一下,如果String可变的话,这种常量池存放字符串,内容相同则不再开辟空间而是指向同一引用的设计思想是否还可行?
是不是即便创建了相同的字符串也要重新分配内存,造成不必要的内存损耗。这就是String自身的"优化"设计思想。
码文不易
你的关注是浩说编程持续更新的动力
浩说编程会做的更好
二、Stirng.intern:从20G到百兆内存消耗
该优化方法分享自全球知名社交平台推特(Twitter)的一名工程师,使用这种方法成功帮助他将String的内存消耗从20G降到几百兆。
我们通过代码来看一下intern方法做了什么:
String a = new String("abc").intern();
String b = new String("abc").intern();
if(a==b) {
System.out.print("a==b");
}
12345
创建 a 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
创建 b 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
而在堆内存中的两个对象,由于没有引用指向它,将会被垃圾回收。所以 a 和 b 引用的是同一个对象。
三、纠正字符串拼接误区
按照正常的理解,由于String不可变,那么每次运算操作都应该产生了一个新的对象,事实真的如此吗?
String str = "a" + "b" + "c";
1
我将上面代码编译成的class文件通过反编译之后的结果:
String str = "abc";
1
所以事实是:在拼接String常量的时候,编译器对代码进行了优化,将字符串合并了。
我们再试一下拼接变量的效果:
String b = "b";
String str = "a" + b + "c";
12
编译之后:
String b = "b";
String str = (new StringBuilder()).append("a").append(b).append("c");
12
虽然编译器对String进行了优化,不过还是建议显示的使用StringBuffer或StringBuilder。
四、字符串分割的最优解
对于字符串的分割,我在上一篇讲到过split()方法中正则表达式的回溯引起的效率问题,所以建议采用indexOf()方法作字符串分割。
作者 | 浩说编程
来源 | 公众号:浩说编程
[ 大厂技术资源 | 研发必备安装包 | 限时免费获取 ]
本文暂时没有评论,来添加一个吧(●'◡'●)