java.lang.OutOfMemoryError 处理大型 CSV 文件时
java.lang.OutOfMemoryError While processing a Large CSV file
我正在使用 java 代码处理一个巨大的 CSV (1GB)。
我的应用程序 运行 在具有 8GB 内存的 2 核机器上。
我正在使用以下命令启动我的应用程序。
java -Xms4g -Xmx6g -cp $CLASSPATH JobSchedulerService
Applcation 启动一个线程从 S3 下载 CSV 并处理它。
应用程序处理文件一段时间但在处理文件的一半时出现 OutOfMemoryError。
我正在寻找一种可以继续处理 CSV 文件并同时保持低内存使用率的方法。
在 CSV 流程中,我正在执行以下步骤:
//Step 1: Download FROM S3
String bucketName = env.getProperty(AWS_S3_BUCKET_NAME);
AmazonS3 s3Client = new AmazonS3Client(credentialsProvider);
S3Object s3object = s3Client.getObject(new GetObjectRequest(bucketName, key));
InputStream inputSteam = s3object.getObjectContent(); //This Stream contains about 1GB of data
//Step 2: Parse CSV to Java
ObjectReader oReader = CSV_MAPPER.readerFor(InboundProcessing.class).with(CSV_SCHEMA);
try (FileOutputStream fos = new FileOutputStream(outputCSV, Boolean.FALSE)) {
SequenceWriter sequenceWriter = CsvUtils.getCsvObjectWriter(InboundProcessingDto.class).writeValues(fos);
MappingIterator<T> mi = oReader.readValues(inputStream)
while (mi.hasNextValue()) {
InboundProcessing inboundProcessing = mi.nextValue();
inboundProcessingRepository.save(inboundProcessing); // this is Spring JPA Entity Save operation. (Almost 3M records so 3M calls)
sequenceWriter.write(inboundProcessingDto); // this is writing to a CSV file on local file system which is uploaded to S3 in next Step
}
} catch (Exception e) {
throw new FBMException(e);
}
1) 将大文件拆分成小文件。
2) 按顺序或并行逐一处理每个文件。
勾选 link 以小尺寸拆分文件:
或
使用 Unix 命令"split for split based on size".
我找到了 OOM 的原因。
虽然我正在以正确的方式阅读文件。完成处理后,逐行读取文件并丢弃旧行。所以这不会造成问题。
问题是当我将相同的内容写入数据库时。
我的代码在事务块中运行,因此在事务完成之前不会释放实体。简而言之,所有 3M 实体都保存在内存中,直到提交事务。
在我怀疑的对象中添加 finalize 方法后,我就得出了这个结论。我所能看到的是 DTOS(临时 Pojo)以非常快的速度被丢弃,但甚至没有一个实体被丢弃。最后突然间所有的实体都被丢弃了。
您还没有关闭InputStream inputSteam
关于s3object.getObjectContent()
获取包含此对象内容的输入流。
注意:该方法很简单 getter,实际上并没有创建流。如果您检索 S3Object,您应该尽快关闭此输入流,因为对象内容不会在内存中缓冲并直接从 Amazon S3 流式传输。此外,未能关闭此流可能会导致请求池被阻塞。
我正在使用 java 代码处理一个巨大的 CSV (1GB)。
我的应用程序 运行 在具有 8GB 内存的 2 核机器上。
我正在使用以下命令启动我的应用程序。
java -Xms4g -Xmx6g -cp $CLASSPATH JobSchedulerService
Applcation 启动一个线程从 S3 下载 CSV 并处理它。 应用程序处理文件一段时间但在处理文件的一半时出现 OutOfMemoryError。
我正在寻找一种可以继续处理 CSV 文件并同时保持低内存使用率的方法。
在 CSV 流程中,我正在执行以下步骤:
//Step 1: Download FROM S3
String bucketName = env.getProperty(AWS_S3_BUCKET_NAME);
AmazonS3 s3Client = new AmazonS3Client(credentialsProvider);
S3Object s3object = s3Client.getObject(new GetObjectRequest(bucketName, key));
InputStream inputSteam = s3object.getObjectContent(); //This Stream contains about 1GB of data
//Step 2: Parse CSV to Java
ObjectReader oReader = CSV_MAPPER.readerFor(InboundProcessing.class).with(CSV_SCHEMA);
try (FileOutputStream fos = new FileOutputStream(outputCSV, Boolean.FALSE)) {
SequenceWriter sequenceWriter = CsvUtils.getCsvObjectWriter(InboundProcessingDto.class).writeValues(fos);
MappingIterator<T> mi = oReader.readValues(inputStream)
while (mi.hasNextValue()) {
InboundProcessing inboundProcessing = mi.nextValue();
inboundProcessingRepository.save(inboundProcessing); // this is Spring JPA Entity Save operation. (Almost 3M records so 3M calls)
sequenceWriter.write(inboundProcessingDto); // this is writing to a CSV file on local file system which is uploaded to S3 in next Step
}
} catch (Exception e) {
throw new FBMException(e);
}
1) 将大文件拆分成小文件。
2) 按顺序或并行逐一处理每个文件。
勾选 link 以小尺寸拆分文件:
或
使用 Unix 命令"split for split based on size".
我找到了 OOM 的原因。 虽然我正在以正确的方式阅读文件。完成处理后,逐行读取文件并丢弃旧行。所以这不会造成问题。
问题是当我将相同的内容写入数据库时。
我的代码在事务块中运行,因此在事务完成之前不会释放实体。简而言之,所有 3M 实体都保存在内存中,直到提交事务。
在我怀疑的对象中添加 finalize 方法后,我就得出了这个结论。我所能看到的是 DTOS(临时 Pojo)以非常快的速度被丢弃,但甚至没有一个实体被丢弃。最后突然间所有的实体都被丢弃了。
您还没有关闭InputStream inputSteam
关于s3object.getObjectContent() 获取包含此对象内容的输入流。
注意:该方法很简单 getter,实际上并没有创建流。如果您检索 S3Object,您应该尽快关闭此输入流,因为对象内容不会在内存中缓冲并直接从 Amazon S3 流式传输。此外,未能关闭此流可能会导致请求池被阻塞。