如何用BufferedReader读取大型CSV至内存?内存溢出问题求助
问题分析与解决
1. JVM参数未生效的核心原因
你犯了两个关键错误:
- 把JVM堆参数写到了IntelliJ的程序参数框里,正确的位置是「Run/Debug Configurations」中的「VM options」栏,程序参数是传给
main方法的参数,不会被JVM解析。 - 参数格式错误:
-Xms=20G里的等号是多余的,正确写法是-Xms20G -Xmx20G。
这两个问题导致你的堆内存配置完全没生效,程序一直使用默认堆大小运行,所以改2M和20G结果一致。
2. 10M行就OOM的内存开销解析
原始文件仅1G,但内存中存储的对象开销远大于原始字节:
- 每个
String包含对象头(约16字节)、char数组(每个char占2字节)、偏移量和长度字段,短字符串的额外占比极高。 - 每个
String[]数组有对象头+元素引用(64位JVM下每个引用8字节),16列的数组仅引用就占128字节。 ArrayList的扩容机制会预留1.5倍的空间,进一步增加内存占用。
10M行×16列的组合,加上这些额外开销,很容易撑爆默认堆(通常几百MB)。
3. 解决方法
第一步:正确配置JVM参数
打开IntelliJ的「Run/Debug Configurations」→ 选中你的运行配置 → 在「VM options」输入框填入:
-Xms20G -Xmx20G
20G堆内存足够容纳16M行的数据集。
第二步:优化内存占用(可选)
如果想进一步降低内存压力,或者修正手动解析CSV的潜在问题(比如字段含逗号时split会出错),可以尝试:
- 用CSV专用解析库:比如OpenCSV,它的解析逻辑更高效,还能处理CSV格式的特殊场景,示例代码:
List<String[]> res = new ArrayList<>(); CSVReader reader = new CSVReader(new InputStreamReader(s3.getObject(s3BucketName, path).getObjectContent())); String[] nextLine; while ((nextLine = reader.readNext()) != null) { res.add(nextLine); } reader.close();
- 转存为原始类型数组:如果字段是数字类型,直接解析为
int/double存入对应数组,彻底避免String的额外开销。 - 使用内存高效集合:比如Eclipse Collections的
ObjectArrayList,比JDK原生ArrayList节省约20%的内存。
内容的提问来源于stack exchange,提问作者Crypto
相关产品推荐
相关产品推荐

