您好,欢迎访问一九零五行业门户网

Java的StringBuilder在高性能场景下怎么正确使用

stringbuilder在高性能场景下的正确用法关于stringbuilder,一般同学只简单记住了,字符串拼接要用stringbuilder,不要用+,也不要用stringbuffer,然后性能就是最好的了,真的吗吗吗吗?
还有些同学,还听过三句似是而非的经验:
1. java编译优化后+和stringbuilder的效果一样;
2. stringbuilder不是线程安全的,为了“安全”起见最好还是用stringbuffer;
3. 永远不要自己拼接日志信息的字符串,交给slf4j来。
1. 初始长度好重要,值得说四次。stringbuilder的内部有一个char[], 不断的append()就是不断的往char[]里填东西的过程。
new stringbuilder() 时char[]的默认长度是16,然后,如果要append第17个字符,怎么办?
用system.arraycopy成倍复制扩容!!!!
这样一来有数组拷贝的成本,二来原来的char[]也白白浪费了要被gc掉。可以想见,一个129字符长度的字符串,经过了16,32,64, 128四次的复制和丢弃,合共申请了496字符的数组,在高性能场景下,这几乎不能忍。
所以,合理设置一个初始值多重要。
但如果我实在估算不好呢?多估一点点好了,只要字符串最后大于16,就算浪费一点点,也比成倍的扩容好。
2. liferay的stringbundler类liferay的stringbundler类提供了另一个长度设置的思路,它在append()的时候,不急着往char[]里塞东西,而是先拿一个string[]把它们都存起来,到了最后才把所有string的length加起来,构造一个合理长度的stringbuilder。
3. 但,还是浪费了一倍的char[]浪费发生在最后一步,stringbuilder.tostring()
// create a copy, don't share the array
return new string(value, 0, count);
string的构造函数会用 system.arraycopy()复制一把传入的char[]来保证安全性不可变性,如果故事就这样结束,stringbuilder里的char[]还是被白白牺牲了。
为了不浪费这些char[],一种方法是用unsafe之类的各种黑科技,绕过构造函数直接给string的char[]属性赋值,但很少人这样做。
另一个靠谱一些的办法就是重用stringbuilder。而重用,还解决了前面的长度设置问题,因为即使一开始估算不准,多扩容几次之后也够了。
4. 重用stringbuilder这个做法来源于jdk里的bigdecimal类(没事看看jdk代码多重要),springside里将代码提取成stringbuilderholder,里面只有一个函数
public stringbuilder getstringbuilder() {
sb.setlength(0);
return sb;
}
stringbuilder.setlength()函数只重置它的count指针,而char[]则会继续重用,而tostring()时会把当前的count指针也作为参数传给string的构造函数,所以不用担心把超过新内容大小的旧内容也传进去了。可见,stringbuilder是完全可以被重用的。
为了避免并发冲突,这个holder一般设为threadlocal,标准写法见bigdecimal或stringbuilderholder的注释。
5. + 与 stringbuilderstring s = “hello ” + user.getname();
这一句经过javac编译后的效果,的确等价于使用stringbuilder,但没有设定长度。
string s = new stringbuilder().append(“hello”).append(user.getname());
但是,如果像下面这样:
string s = “hello ”;
// 隔了其他一些语句
s = s + user.getname();
每一条语句,都会生成一个新的stringbuilder,这里就有了两个stringbuilder,性能就完全不一样了。如果是在循环体里s+=i; 就更加多得没谱。
据r大说,努力的jvm工程师们在运行优化阶段, 根据+xx:+optimizestringconcat(jdk7u40后默认打开),把相邻的(中间没隔着控制语句) stringbuilder合成一个,也会努力的猜长度。
所以,保险起见还是继续自己用stringbuilder并设定长度好了。
6. stringbuffer 与 stringbuilderstringbuffer与stringbuilder都是继承于abstractstringbuilder,唯一的区别就是stringbuffer的函数上都有synchronized关键字。
那些说stringbuffer “安全”的同学,其实你几时看过几个线程轮流append一个stringbuffer的情况???
7. 永远把日志的字符串拼接交给slf4jlogger.info(hello {}, user.getname());
对于不知道要不要输出的日志,交给slf4j在真的需要输出时才去拼接的确能省节约成本。
但对于一定要输出的日志,直接自己用stringbuilder拼接更快。因为看看slf4j的实现,实际上就是不断的indexof({}), 不断的substring(),再不断的用stringbuilder拼起来而已,没有银弹。
ps. slf4j中的stringbuilder在原始message之外预留了50个字符,如果可变参数加起来长过50字符还是得复制扩容......而且stringbuilder也没有重用。
以上就是java的stringbuilder在高性能场景下怎么正确使用的详细内容。
其它类似信息

推荐信息