Java String拼接性能分析

StringAdd, StringBuffer, StringBuilder, StringBuilderHelper性能对比分析

测试环境

硬件

CPU

Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz

基准速度:   1.80 GHz
插槽: 1
内核: 4
逻辑处理器:  8
虚拟化:    已启用
L1 缓存:  256 KB
L2 缓存:  1.0 MB
L3 缓存:  6.0 MB

内存

8.0 GB

速度: 2133 MHz
已使用的插槽: 2/2
外形规格:   Row of chips
为硬件保留的内存:   154 MB

软件

IDEA ULTIMATE 2018.3
JMH Benchmark框架 1.21

测试类

/**
 * 比较字符串直接相加和StringBuilder的效率
 */
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
@State(Scope.Thread)
@Threads(1)
@Fork(1)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class StringBuilderBenchmark {
    private String repeatItem;
    private int repeatTimes;
    
    @Setup
    public void init() {
        repeatItem = "Benchmark";
        repeatTimes = 100;
    }

    @Benchmark
    public void stringAdd(Blackhole bh) {
        String a = "";
        for (int i = 0; i < repeatTimes; i++) {
            a += repeatItem;
        }
        bh.consume(a);
    }

    @Benchmark
    public void stringBufferAdd(Blackhole bh) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < repeatTimes; i++) {
            sb.append(repeatItem);
        }
        bh.consume(sb.toString());
    }

    @Benchmark
    public void stringBuilderAdd(Blackhole bh) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < repeatTimes; i++) {
            sb.append(repeatItem);
        }
        bh.consume(sb.toString());
    }

    @Benchmark
    public void stringBuilderHelperAdd(Blackhole bh) {
        StringBuilder sb = StringBuilderHelper.getStringBuilder();
        for (int i = 0; i < repeatTimes; i++) {
            sb.append(repeatItem);
        }
        bh.consume(sb.toString());
    }
}

public final class StringBuilderHelper {
    private static final ThreadLocal<StringBuilder> TLSB = ThreadLocal.withInitial(StringBuilder::new);

    private StringBuilderHelper() {
    }

    public static StringBuilder getStringBuilder() {
        StringBuilder stringBuilder = TLSB.get();
        stringBuilder.setLength(0);
        return stringBuilder;
    }
}

生成短字符串

"Benchmark"重复拼接20次

这里取平均耗时,单位微秒,下同。

@Setup
public void init() {
    repeatItem = "Benchmark";
    repeatTimes = 20;
}
 
Benchmark                                      Mode  Cnt  Score   Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  0.406 ± 0.010  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5  0.262 ± 0.005  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5  0.259 ± 0.033  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.152 ± 0.020  us/op

"BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接20次

@Setup
public void init() {
    repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
    repeatTimes = 20;
}

Benchmark                                      Mode  Cnt  Score   Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  1.717 ± 0.010  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5  0.861 ± 0.007  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5  0.846 ± 0.016  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.249 ± 0.023  us/op

生成中长字符串

"Benchmark"重复拼接100次

@Setup
public void init() {
    repeatItem = "Benchmark";
    repeatTimes = 100;
}
 
Benchmark                                      Mode  Cnt  Score   Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  8.103 ± 0.128  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5  1.198 ± 0.010  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5  1.113 ± 0.025  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.663 ± 0.009  us/op

"BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接100次

@Setup
public void init() {
    repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
    repeatTimes = 100;
}

Benchmark                                      Mode  Cnt   Score   Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  38.761 ± 1.753  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5   3.432 ± 0.036  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5   3.240 ± 0.043  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5   1.367 ± 0.021  us/op

生成长字符串

"Benchmark"重复拼接1000次

@Setup
public void init() {
    repeatItem = "Benchmark";
    repeatTimes = 1000;
}

Benchmark                                      Mode  Cnt    Score    Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  761.627 ± 16.109  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5   10.885 ±  0.144  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5   10.016 ±  0.179  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5    7.148 ±  0.107  us/op

"BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接1000次

@Setup
public void init() {
    repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
    repeatTimes = 1000;
}

Benchmark                                      Mode  Cnt     Score    Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  4035.181 ± 59.044  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5    35.703 ±  9.170  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5    32.452 ±  5.626  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5    17.628 ±  5.808  us/op

生成超长字符串

"Benchmark"重复拼接10000次

@Setup
public void init() {
    repeatItem = "Benchmark";
    repeatTimes = 10000;
}
Benchmark                                      Mode  Cnt      Score      Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  78718.895 ± 1009.605  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5    131.177 ±   37.317  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5    119.158 ±   21.876  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5     81.608 ±    5.685  us/op

"BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接10000次

@Setup
public void init() {
    repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
    repeatTimes = 10000;
}
Benchmark                                      Mode  Cnt       Score        Error  Units
StringBuilderBenchmark.stringAdd               avgt    5  704055.930 ± 991698.568  us/op
StringBuilderBenchmark.stringBufferAdd         avgt    5     426.180 ±      1.483  us/op
StringBuilderBenchmark.stringBuilderAdd        avgt    5     420.615 ±      2.914  us/op
StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5     177.804 ±     17.385  us/op

生成超长字符串+多线程

"BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接10000次,10个线程

@Threads(10)
@Setup
public void init() {
    repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
    repeatTimes = 10000;
}

Benchmark                                       Mode  Cnt        Score       Error  Units
StringBuilderBenchmark.stringAdd                avgt    5  6877607.658 ± 68654.544  us/op
StringBuilderBenchmark.stringBufferAdd          avgt    5     3792.719 ±    33.263  us/op
StringBuilderBenchmark.stringBuilderAdd         avgt    5     3780.851 ±    79.109  us/op
StringBuilderBenchmark.stringBuilderHelperAdd   avgt    5     1387.908 ±    67.224  us/op

结果分析

  • 短字符串,如"Benchmark"重复 20次,String+的性能是其他三种方法的1/2,差距不是太大。
  • 中长字符串开始,如"Benchmark"重复100次,String+的性能被拉开。
  • StringBuffer和StringBuilder在单线程场景下,性能非常接近,后者略优。
  • StringBuilderHelper(ThreadLocal版的StringBuilder)是性能最好的版本,尤其是大颗粒大字符串情况下("BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复10000次对比"Benchmark"重复10000次),相较于StringBuilder和StringBuffer的性能,提升一倍多。
  • StringBuilderHelper在线程池下的效率会更高(因为线程不销毁,StringBuilder实例不会被销毁,能够最大程度复用)。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 12月6-7日,团委在相城和东山校区开展了主题为 “青春奉献美丽中国梦,团聚共绘青春苏农图” 特别团日活动。园艺科...
    泗水留年阅读 576评论 0 0
  • 成都,我的第二故乡。我的故乡她不美,该怎么形容她呢? 大概是2005年,第一次来成都。怀着各种心情,除了点点恐惧,...
    了梦一川阅读 244评论 0 1
  • 如果夜色沉寂 是否能够拾起 夕阳里的记忆 字字端祥,事无巨细 如果爱有天意 能否不再背离 倾注一生的意义 故事柔软...
    子觀阅读 239评论 0 3
  • 我是一个九零后,同时我也是个淘宝店主。我从一个连开店流程都要靠百度的小白卖家。到月赚三万多的小资卖家。 对的,我的...
    轻奢女王阅读 622评论 0 0