java - 如何避免使用 toString().getBytes("UTF-8") 来避免 OOM 错误?有没有更好的方法从 StringWriter 转换为 byte[]?
问题描述
我有一个生成报告的报告 Web 应用程序。应用程序从数据库中获取数据并将数据存储到StringWriter
对象中。我必须以字节数组格式获取这些数据以创建 csv 文件并将其发送到浏览器。
下面是代码片段
return new FileTransfer(fileName, reportType.getMimeType(),
new ByteArrayInputStream(generateCSV(reportType, grid, new DataList(), params).toString().getBytes("UTF-8")));
wheregenerateCSV
返回一个StringWriter
对象,然后将其转换为我正在调用的字节数组toString
,然后是getBytes()
方法。下面是该generateCSV
方法的样子
StringWriter generateCSV(ReportType reportType, GridConfig grid, DataList dataList, String params) {......}
问题是当我的报告有大量记录(超过 100 万条)时,该getBytes()
方法会失败
java.lang.OutOfMemoryError:请求的数组大小超过 VM 限制
转换为 String 对象时的整个报表数据具有大量字符(数十亿)。该.getBytes("UTF-8")
方法将其转换为数组,每个数组元素为一个字符。对于 100 万条记录,该字符超出了 MAX JVM ARRAY 大小限制 ( https://plumbr.io/outofmemoryerror/requested-array-size-exceeds-vm-limit )。
现在如何避免使用toString().getBytes("UTF-8")
以避免OOM错误?有没有更好的方法从 转换为字节数组StringWriter
?
解决方案
generateCSV
收到as aStringWriter
的结果很奇怪 首选的解决方案是让该方法在生成时写入目标,这样您就不会在内存中拥有全部内容。
无论哪种情况,您都应该求助于FileTransfer(String, String mimeType, OutputStreamLoader)
构造函数,以便在OutputStream
写入实际数据时接收目标。
当您无法避免中间StringWriter
时,您至少应该避免调用toString
它,因为构造 aString
意味着创建整个缓冲区的副本。
所以解决方案可能如下所示:
return new FileTransfer(fileName, reportType.getMimeType(), new OutputStreamLoader() {
public void close() {}
public void load(OutputStream out) throws IOException {
// the best would be to let generateCSV write to out directly
// otherwise use:
StringBuffer sb = generateCSV(reportType, grid, new DataList(), params).getBuffer();
Writer w = new OutputStreamWriter(out, "UTF-8")
final int bufSize = 8192;
for(int s = 0, e; s < sb.length(); s = e) {
e = Math.min(sb.length(), s + bufSize);
w.write(sb.substring(s, e));
}
w.flush(); // let the caller close the OutputStream
}
});
的替代方案StringWriter
是CharArrayWriter
,它有一个writeTo(Writer out)
,它消除了实现手动复制循环的需要,并且可能更有效。但是,如前所述,重构generateCSV
直接写入目标会更好。
推荐阅读
- sql - 为什么这个双重否定查询与肯定查询不同?
- vue.js - 调用 done() 时 Vue Javascript 转换不起作用
- slack - Slack 是否提供对 IP 白名单的支持?
- html - 有没有办法删除一个 div 但保留它的元素?
- chromium - 如何在基于铬的网络浏览器中找到“铬版本”?
- apache-kafka - Kafka 和 Kafka Connect 部署环境
- c# - 如何使用 Angular 5 和 asp.net 核心从 Angular 向端点发送电子邮件
- c# - 通过字符串 delim 将 5k 多个对象发送到 SQL PROC 参数,然后对 Query 中的每个对象进行拆分、解析和循环
- matlab - 我可以保存 RandStream 并在以后加载它以继续模拟吗?
- python - 无法使用 PIL 保存图像