我正在学习如何使用JMH对事物进行微基准测试。我从看似简单的内容开始:StringBuildervs的字符串连接String +=。
据我了解,我应该制作一个State包含的实例的对象,StringBuilder因为我不想对其构造函数进行基准测试(无论如何,我也不想每次迭代都为空)。这同样适用于String +=测试-我想String在我的对象State与新的字符串并置。
这是我的代码:
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Test {
@State(Scope.Thread)
public static class BenchmarkState {
public StringBuilder builder;
public String regularString;
@Setup(Level.Iteration)
public void setup() {
builder = new StringBuilder();
regularString = "";
}
}
@Benchmark
public String stringTest(BenchmarkState state) {
state.regularString += "hello";
return state.regularString;
}
@Benchmark
public String stringBuilderTest(BenchmarkState state) {
state.builder.append("hello");
return state.builder.toString();
}
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(Test.class.getSimpleName())
.forks(1)
.timeUnit(TimeUnit.MILLISECONDS)
.mode(Mode.Throughput)
.measurementTime(TimeValue.seconds(10))
.build();
new Runner(opt).run();
}
}
它有效,但是我在想-我不想.toString()在每次迭代结束时都调用。我仅测试串联。因此,我决定通过返回null而将其删除。
但是,这是在第一次热身迭代期间发生的:
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
我知道,如果JMHStringBuilder尽可能快地追加,我将很快耗尽内存,因此我对这个OutOfMemoryError问题并不感到惊讶。但是我不明白为什么要builder.toString()修复它。
所以我的问题是:
为什么要builder.toString()避免出现OutOfMemoryError问题?StringBuilder还是不将所有字符都保留在内存中吗?
假设我既不希望StringBuilder的构造.toString()方法也不希望其 方法成为基准测试的一部分,那么如何正确编写此测试?
扬帆大鱼
相关分类