You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JMH对Java静态方法做微基准测试时触发IndexOutOfBoundsException

问题分析与解决方案

首先,这个IndexOutOfBoundsException的直接原因很明确:你尝试访问的索引(902)超出了id列表的大小(0)——也就是说,你的列表根本没有被填充任何元素,是空的。

为什么列表是空的?

问题出在你的JMH @Setup方法上:你把它声明成了static final,但JMH的@Setup方法不能是静态的,也不能用final修饰。

JMH的工作机制是创建基准测试类的实例,然后调用实例的@Setup方法来完成初始化工作。静态方法属于类本身,不会被JMH的实例调用,所以你的setup()代码从头到尾都没执行过,那些id、property_address等列表自然还是空的。

另外还有两个潜在的坑:

  1. 你把n声明成了静态变量,它会在类加载时就生成,但此时列表还没填充,虽然这次报错不是因为这个,但逻辑上不合理。
  2. 你的setup()方法里没有给first_name和last_name添加元素,但基准方法里却调用了它们的get()方法,就算前面的问题解决了,这里也会触发同样的越界异常。

修正后的代码

下面是调整后的完整基准测试代码,我标注了关键的修改点:

import org.openjdk.jmh.annotations.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

// 必须添加@State注解,指定作用域,JMH才能正确管理实例和初始化
@State(Scope.Benchmark)
public class MyBenchmark {
    private Random rand = new Random();
    private int n; // 改成实例变量,不要静态
    // 把列表也改成实例变量(当然静态也能工作,但配合@State用实例变量更符合JMH的设计)
    private List<Integer> id = new ArrayList<>();
    private List<Integer> property_address = new ArrayList<>();
    private List<Integer> first_name = new ArrayList<>();
    private List<Integer> last_name = new ArrayList<>();
    private List<Integer> email = new ArrayList<>();
    private List<Integer> owner_address = new ArrayList<>();
    private List<Integer> price = new ArrayList<>();
    private List<Integer> date_sold = new ArrayList<>();

    // 移除static和final,改成普通实例方法
    @Setup
    public void setup() {
        int x = 1000;
        for (int i = 0; i < x; i++) {
            id.add(rand.nextInt(500) + 1);
            property_address.add(rand.nextInt(1000) + 1);
            first_name.add(rand.nextInt(1000) + 1); // 补充填充first_name
            last_name.add(rand.nextInt(1000) + 1); // 补充填充last_name
            email.add(rand.nextInt(1000) + 1);
            owner_address.add(rand.nextInt(1000) + 1);
            price.add(rand.nextInt(1000) + 1);
            date_sold.add(rand.nextInt(1000) + 1);
        }
        // 把n的初始化移到这里,确保列表已经填充完成后再生成有效索引
        n = rand.nextInt(999) + 1;
        System.out.println("Setup Complete");
    }

    @Benchmark
    public void getPropertybyId() {
        // 注意:基准测试里尽量不要用System.out.println!
        // IO操作会严重干扰测试结果,如果你只是要验证逻辑,可以保留,但正式测试一定要删掉
        System.out.println(id.get(n) + ", " + property_address.get(n) + ", " + first_name.get(n) + ", " + last_name.get(n) + ", " + email.get(n) + ", " + owner_address.get(n) + ", " + price.get(n) + ", " + date_sold.get(n));
    }
}

额外的优化建议

  • 去掉基准方法里的System.out.println:IO操作的开销远大于List的get()操作,会让你的基准测试结果完全失真。如果需要验证逻辑,可以在调试时保留,正式测试一定要移除。
  • 如果你想更精准地测试get()操作的性能,可以把基准方法的返回值改成这些元素的组合(比如返回一个字符串或者自定义对象),JMH会自动处理,不会影响测试结果。

内容的提问来源于stack exchange,提问作者Kabira Suleman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:38:50