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

Java读取大Parquet文件的内存溢出问题及相关疑问

问题描述

我正在编写Java程序逐行处理一个Parquet大文件,该文件包含约130万行、3000列双精度浮点数,文件大小约6.6G。按照教程实现读取代码时,运行抛出OutOfMemoryError,推测是row group加载了全部130万行导致内存不足。

之后用pandas重新保存该文件:

  • 设置row_group_size=1000时,打开文件直接触发数组长度过大的OutOfMemoryError
  • 设置row_group_size=10000时,读取部分行后仍出现堆内存溢出,但移除simpleGroups.add(simpleGroup)这行代码后能完成读取(耗时10分28秒,而pandas读取仅需18.6秒)

疑问列表

  1. 机器有54G空闲内存,已设置VM参数-Xmx30g和-Xms30g,为何row_group_size=10000时仍出现堆内存溢出?
  2. 为何row_group_size较小时会触发不同的错误?
  3. 若需逐行处理数据,是否必须通过设置小row_group_size避免预加载大量行占用内存?

问题解答

1. 为何row_group_size=10000时仍堆内存溢出?

10000行×3000列的双精度浮点数,单row group的原始数据量是10000×3000×8字节=240MB,但Java读取Parquet时的内存开销远不止这个数:

  • SimpleGroup对象本身有对象头、引用等额外开销,每列的数值如果用Double包装类(而非原生double)存储,内存占用直接翻倍;
  • 再加上Parquet读取过程中的中间缓存(列解码器的临时缓冲区、字典映射表等),以及JVM本身的元空间、GC预留内存,30G堆内存其实没那么充裕。尤其是你之前把每个SimpleGroup都加到simpleGroups列表里,等于把整个row group的所有行都留在内存,多个row group叠加起来,很容易就把堆内存撑爆。
    你移除simpleGroups.add(simpleGroup)后能跑完,也直接证明了是这些缓存的SimpleGroup实例占满了内存。

2. row_group_size较小时为何触发不同错误?

当row_group_size=1000时,每个row group的列数还是3000,Parquet的Java库在处理列数据时,可能出现两种情况:要么是代码逻辑错误(比如误把列数当成行数创建数组),要么是库本身对小row group的处理存在bug,导致创建了远超预期的大数组,进而触发“数组长度过大”的错误。而row group较大时,内存是被大量对象实例逐步占满,所以表现为普通的堆内存溢出。

3. 逐行处理是否必须设置小row_group_size?

不是必须,但设置合适的row_group_size能降低单次加载的内存压力。更核心的是不要把所有行都缓存到内存里:

  • 读取每一行(或每一个SimpleGroup)后,立即处理数据,处理完就丢弃该对象,别把它加到列表里长期持有;
  • 尽量使用Parquet的面向列的流式读取API,比如用ParquetReader的read()方法逐行读取,而非一次性加载整个row group;
  • 优先用原生类型(比如double[])存储列数据,避免使用Double包装类,减少额外内存开销。
    如果原文件的row group过大,用pandas重新拆分时,选择50000左右的row_group_size(平衡内存和IO效率)会更合理,但核心始终是处理完就释放内存,不要缓存无用数据。

内容的提问来源于stack exchange,提问作者Harry Braviner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:50:36