为什么.toString()似乎为StringBuilder修复了OutOfMemoryError异常? [英] Why does .toString() seem to fix an OutOfMemoryError exception for StringBuilder?

查看:339
本文介绍了为什么.toString()似乎为StringBuilder修复了OutOfMemoryError异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在学习如何使用JMH对事物进行微基准测试.我从一个看似简单的东西开始:StringBuilder vs String +=的字符串连接.

I am learning how to microbenchmark things with JMH. I started with something seemingly simple: string concatenation for StringBuilder vs String +=.

根据我的理解,我应该制作一个包含StringBuilder实例的State对象,因为我不想基准化其构造函数(也不想每次迭代都为空). String +=测试也是如此-我希望将State中的String对象与新字符串连接起来.

From my understanding, I should make a State object that contains an instance of StringBuilder because I don't want to benchmark its constructor (nor do I want to an empty one every iteration anyway). Same goes for the String += test - I want a String object in my State to be concatenated with new strings.

这是我的代码:

@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来删除它.

It works, but I was thinking - I don't want to call .toString() at the end of every iteration. I am testing concatenation only. So I decided to remove it by just returning null instead.

但是,这是在第一次热身迭代期间发生的:

But then, this happens during the first warmup iteration:

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)

我知道,如果JMH尽可能快地附加到StringBuilder,我将很快耗尽内存,因此对于OutOfMemoryError问题,我并不感到惊讶.但是我不明白为什么builder.toString()会解决它.

I understand that I would run out of memory pretty quickly if JMH is appending to StringBuilder as fast as it can, so I'm not surprised by the OutOfMemoryError issue. But I don't understand why does builder.toString() fix it.

所以我的问题是:

  • 为什么builder.toString()可以避免OutOfMemoryError问题? StringBuilder仍然不把所有字符都保留在内存中吗?

  • Why does builder.toString() avoid an OutOfMemoryError issue? Doesn't StringBuilder still keep all the characters in memory regardless?

假设我既不希望StringBuilder的构造函数也不希望其.toString()方法成为基准测试的一部分,那么我该如何正确编写此测试?

Assuming that I do NOT want neither StringBuilder's constructor nor its .toString() method to be part of the benchmark, how do I properly write this test?

推荐答案

调用toString()需要花费时间,并产生垃圾,需要运行GC,从而进一步降低了代码的速度.

Calling toString() takes time, and generates garbage, requiring GC runs, further slowing down the code.

由于测试有时间限制,因此这些减速可能会导致测试在消耗所有内存之前停止.如果增加时间限制,即使使用toString,代码也可能因OOM而失败,只会花费很多时间.

Since testing has a time limit, those slowdowns likely cause test to stop before it consumes all memory. If you increase the time limit, the code will likely fail with OOM even with the toString, it will just take a LOT longer.

这篇关于为什么.toString()似乎为StringBuilder修复了OutOfMemoryError异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆