Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java如何使用+;优化字符串连接;?_Java_String_Optimization_Stringbuilder_String Concatenation - Fatal编程技术网

Java如何使用+;优化字符串连接;?

Java如何使用+;优化字符串连接;?,java,string,optimization,stringbuilder,string-concatenation,Java,String,Optimization,Stringbuilder,String Concatenation,我知道在最近的Java版本中,字符串连接 String test = one + "two"+ three; 将得到优化以使用StringBuilder 但是,是否会在每次点击此行时生成一个新的StringBuilder,还是会生成一个单线程本地StringBuilder,然后用于所有字符串连接 换句话说,我可以通过创建自己的线程本地StringBuilder来提高频繁调用的方法的性能以供重用,还是这样做不会有显著的好处 我可以为此编写一个测试,但我想知道它可能是特定于编译器/JVM的,还是可

我知道在最近的Java版本中,字符串连接

String test = one + "two"+ three;
将得到优化以使用
StringBuilder

但是,是否会在每次点击此行时生成一个新的
StringBuilder
,还是会生成一个单线程本地StringBuilder,然后用于所有字符串连接

换句话说,我可以通过创建自己的线程本地StringBuilder来提高频繁调用的方法的性能以供重用,还是这样做不会有显著的好处


我可以为此编写一个测试,但我想知道它可能是特定于编译器/JVM的,还是可以更一般地回答的?

据我所知,没有编译器生成代码重用
StringBuilder
实例,最明显的是
javac
和ECJ不生成重用代码

重要的是要强调,不重复使用是合理的。假设从
ThreadLocal
变量检索实例的代码比从TLAB进行普通分配要快是不安全的。即使尝试添加回收该实例的本地gc循环的潜在成本,只要我们能够确定其在成本中所占的比例,我们也无法得出结论

因此,试图重用构建器的代码将更加复杂,浪费内存,因为它使构建器保持活力,而不知道它是否会被实际重用,也没有明显的性能优势

特别是当我们考虑上述语句

  • 像HotSpot这样的JVM都有Escape分析,它可以完全省去像这样的纯本地分配,还可以省去数组大小调整操作的复制成本
  • 这种复杂的JVM通常也有专门用于基于
    StringBuilder
    的连接的优化,当编译的代码遵循公共模式时,这种优化效果最好


使用Java9,情况将再次发生变化。然后,字符串连接将被编译为
invokedynamic
指令,该指令将在运行时链接到JRE提供的工厂(请参阅)。然后,JRE将决定代码的外观,这允许根据特定的JVM对代码进行定制,包括缓冲区重用,如果它对特定的JVM有好处的话。这还将减少代码大小,因为它只需要一条指令,而不需要分配序列和对
StringBuilder

的多次调用。您会惊讶于jdk-9字符串连接付出了多少努力。第一个javac发出一个
invokedynamic
,而不是对
StringBuilder\append
的调用。invokedynamic将返回一个
CallSite
,其中包含一个MethodHandle(实际上是一系列MethodHandles)

因此,字符串连接的实际操作决定被移动到运行时。缺点是第一次连接字符串时速度会慢一些(对于相同类型的参数)

然后,在连接字符串时,可以选择一系列策略(可以通过
java.lang.invoke.stringConcat
参数覆盖默认策略):

默认策略是:
MH\u INLINE\u size\u EXACT
,这是一个野兽

它使用包私有构造函数构建字符串(最快):

首先,该策略创建所谓的过滤器;这些基本上是将传入参数转换为字符串值的方法句柄。正如人们所料,这些MethodHandle存储在一个名为
Stringifiers
的类中,在大多数情况下,该类生成一个MethodHandle,该类调用:

String.valueOf(YourInstance)
因此,如果您有3个要连接的对象,那么将有3个MethodHandles委托给
String.valueOf(YourObject)
,这实际上意味着您已将对象转换为字符串。 这个类中有一些我仍然无法理解的调整;就像需要有单独的类
StringifierMost
(只转换为字符串引用、浮点和双精度)和
StringifierAny

因为
MH_INLINE_size_EXACT
表示字节数组计算为精确大小;有一种计算方法

这是通过
StringConcatHelper#mixLen
中的方法实现的,这些方法获取输入参数的字符串化版本(References/float/double)。此时,我们知道最终字符串的大小。实际上我们并不知道,我们有一个方法句柄来计算它

字符串jdk-9中还有一个值得一提的变化——添加了
coder
字段。这是计算字符串的大小/相等性/字符所必需的。因为尺寸需要它,我们也需要计算它;这是通过
StringConcatHelper#mixCoder
完成的

此时可以安全地委托MethodHandle来创建ur数组:

    @ForceInline
    private static byte[] newArray(int length, byte coder) {
        return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
    }
@ForceInline
私有静态字节[]新数组(整数长度,字节编码器){

返回(字节[])不安全。分配InInitializeDarray(byte.class,length在连接表达式时要注意重入性。上次我检查时,它相当愚蠢,迫使
StringBuilder
反复重新分配。但这是Oracle的JDK特有的,查看结果字节码,因此没有考虑JVM可能做的任何优化。我的规则是:99.999%的时间你不在乎当然,对于你关心的.001%,使用显式的
StringBuilder
分配足够大的空间来处理整个结果。除非你做的字符串操作比那一行多得多,否则我同意T.J.:99.999%的时间你看不到任何差异。JVM实际上会将所有内存作为本地内存分配给线程(直到它需要与另一个线程共享),iiuc,所以您的线程
String.valueOf(YourInstance)
    @ForceInline
    private static byte[] newArray(int length, byte coder) {
        return (byte[]) UNSAFE.allocateUninitializedArray(byte.class, length << coder);
    }