历史数据迁移脚本一次把 10 万条记录 submit 进去,跑到第 6 万条报 java.lang.OutOfMemoryError: Java heap space,堆只给了 1GB。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

for (int i = 0; i < list.size(); i += 500) {
    List<Row> batch = list.subList(i, Math.min(i + 500, list.size()));
    pool.invokeAll(batch.stream().map(CallableTask::new).collect(toList()));
}

每个任务都是一个对象,加上闭包捕获的记录和结果包装,单条占几百字节,十万条就是几十 MB 的常驻。队列还没消费完又继续加,Future 列表也在收集引用。这一点先确认清楚,后面就不用反复试。

「批量提交内存暴涨」相关的日志我一般连着看前后五分钟,单看一条报错经常判断不出是哪一步失败。把这次用到的命令存进笔记,比记住结论有用,换个环境还能照着走。

改完隔一天再看一次,当场正常不代表能稳住。

具体做法是,改成分批提交 每批 500 条 提交完等这批结束再下一批;不保留 Future 列表 或者只保留计数;用有界队列加 CallerRunsPolicy 自然限速;跑之前把堆和批量大小一起调 观察老年代增长。

改完照着核一遍,同样十万条数据跑完不 OOM,老年代在两次 GC 之间没有持续上涨。

作者 9823189