JMH基准测试ArrayList.add方法时出现OutOfMemoryError问题
问题根源
- 你错误使用了
@Setup(Level.Trial)注解:该级别代表整个测试周期(包含所有预热、测量迭代)仅执行一次列表初始化,后续所有@Benchmark方法的调用都是往同一个全局列表追加元素,全程不会清空列表。 - 你的测试采用吞吐量模式,单轮预热迭代就要连续跑5秒,从错误日志能看到每秒插入量级达2200万次,仅几秒钟列表长度就会突破千万级,ArrayList扩容时需要申请连续大数组,很快就会耗尽4G堆内存触发OOM。
- 你定义的
iterations参数完全没有在测试逻辑中用到,实际运行是无限制插入,和你预期的固定次数插入测试逻辑完全不符。
修复方案
方案1:调整Setup级别,避免内存无限累积
将Setup的生效级别改为每次迭代前重新初始化列表,避免单轮迭代内列表无限扩容:
// 仅修改@Setup的级别参数即可 @Setup(Level.Iteration) public void setup() { arrayList = new ArrayList<>(); linkedList = new LinkedList<>(); }
如果单轮迭代时间还是设置为5秒,依然可能出现OOM,建议同步缩短单轮迭代时长,或者改用第二种方案。
方案2:按指定插入次数测试,符合你预设的参数逻辑
修改基准方法,每次调用都新建列表,插入指定数量元素后丢弃,完全避免内存泄漏,同时利用你定义的iterations参数:
@State(Scope.Benchmark) @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 5, time = 5) @Measurement(iterations = 10, time = 5) @OutputTimeUnit(TimeUnit.MILLISECONDS) public class ListAddBenchmark { private int NUMBER = 123; @Param({"1000","100000","1000000"}) public int iterations; // 注入JMH提供的Blackhole避免JIT优化掉未使用的列表对象 @org.openjdk.jmh.infra.Blackhole public Blackhole blackhole; @Benchmark public void arrayListBenchmark() { List<Integer> arrayList = new ArrayList<>(); for (int i = 0; i < iterations; i++) { arrayList.add(NUMBER); } blackhole.consume(arrayList); } @Benchmark public void linkedListBenchmark() { List<Integer> linkedList = new LinkedList<>(); for (int i = 0; i < iterations; i++) { linkedList.add(NUMBER); } blackhole.consume(linkedList); } }
额外注意
如果要测试列表已有指定数量元素时的尾插性能,可以在@Setup(Level.Iteration)方法中先给列表填充对应数量的初始元素,再执行插入测试,依然要保证每次迭代都会重新初始化列表,避免元素持续累积。
内容的提问来源于stack exchange,提问作者Toby Cook
相关产品推荐
相关产品推荐

