当我的类结构如下时,我最终遇到OutOfMemory错误。
DataHandler由8个线程的固定池调用(没有外部线程池管理。固定池创建一次,执行一次,并且如果一个线程死亡,则不创建新线程)。一次有多个线程调用DataHandler,但是由于byteArrayOutputStreamBuffer是threadLocal,因此每个线程都有自己的本地缓冲区。每个线程获取数据,调用HandleData(),并在完成后重复循环。
传递的数据大小为2 GB。因此,预计将占用的总内存最多为(2 GB +字节数组流的大小)*线程数。数组流的最大大小应为4 GB(由于内存大小的调整,数据量增加了一倍)。因此,预期的总堆为6 * 8 = 48 GB。堆配置为可以处理更多(我已经尝试了高达300 GB),但是这个问题仍然存在。
public class DataHandler {
private static ThreadLocal<ByteArrayOutputStream> byteArrayOutputStreamBuffer =
new ByteArrayOutputStream();
void HandleData(byte[] data) {
ByteArrayOutputStream byteArrayOutputStream = byteArrayOutputStreamBuffer.get();
File tempFile = new File(getFileName());
try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
byteArrayOutputStream.write(data);
fileOutputStream.write(byteArrayOutputStream.toByteArray());
} finally {
byteArrayOutputStream.reset();
}
}
}
如果删除中间的ByteArrayOutputStream,则没有OOM。我试图找到一种解释,为什么ByteArrayOutputStream导致OOM。
编辑:我看到toByteArray()也将增加2 GB,因此总数将是64 GB。
答案 0 :(得分:1)
ByteArrayOutputStream
最多可以容纳Integer.MAX_VALUE - 8
个字节(比2Gb少8个字节),因为它将数据存储在单个byte[]
中,并且数组长度限制为该值。
如果您尝试放入更多数据,则会抛出OutOfMemomoryError
。
由于要输入大量数据,因此可能就是这样。
在这种情况下,您不能使用ByteArrayOutputStream
。
但是您为什么需要它?您为什么不只将FileOutputStream
存储在ThreadLocal中?