[英]Still getting "OutOfMemoryError: Java heap space" exception even when increasing the JVM Heap Space to 8GB
我在下面有一个截图代码可以将文件上传到另一个存储。 它适用于小于 1GB 数据的文件。 但是,它会暴露“OutOfMemoryError: Java heap space”异常,文件大小 > 1GB(我用 1.2GB 和 1.5GB 验证过)
搜索一些帖子后,我将 JVM 堆空间增加到 8GB。 但是,我仍然看到异常。
我的机器是 16GB RAM
JVM版本和Java Runtime Environment设置如下
这是片段代码:
protected boolean uploadFile(File file) throws BaseAFException {
try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) {
PayloadMessage payload = new PayloadMessage();
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
int bufferSize = 1024;
if (bufferSize > channel.size()) {
bufferSize = (int) channel.size();
}
ByteBuffer buff = ByteBuffer.allocate(bufferSize);
while (channel.read(buff) > 0) {
out.write(buff.array(), 0, buff.position());
buff.clear();
}
//the exception exposes before the debugger hits the line below
payload.setContent(out.toByteArray());
}
//Upload file will be handled here
return true;
} catch (OverlappingFileLockException e) {
return false;
} catch (Exception e) {
throw new Exception("Could not upload file " + file.getAbsolutePath(), e);
}
}
异常堆栈跟踪:
Throwable : java.lang.OutOfMemoryError: Java heap space java.util.concurrent.ExecutionException: java.lang.OutOfMemoryError: Java heap space
at java.util.concurrent.FutureTask.report(Unknown Source)
at java.util.concurrent.FutureTask.get(Unknown Source)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Unknown Source)
at java.io.ByteArrayOutputStream.grow(Unknown Source)
at java.io.ByteArrayOutputStream.ensureCapacity(Unknown Source)
at java.io.ByteArrayOutputStream.write(Unknown Source)
如果您能给我一些建议,我将不胜感激!
请注意, 自 Java 7 以来,仅使用RandomAccessFile
来获取FileChannel
已过时。
您将文件的内容复制到ByteArrayOutputStream
中。 完成此操作后, ByteArrayOutputStream
将拥有一个至少与复制数据一样大的数组,但由于在需要增加内部容量时不知道最终大小,因此它可能高达所需大小的两倍。
然后,你调用它的toByteArray()
,它的文档说“创建一个新分配的字节数组。 ”。
因此,从toByteArray()
返回后,您使用的堆 memory 是实际文件大小的两到三倍。
ByteArrayOutputStream
保存整个文件,但可能具有所需容量的两倍toByteArray()
返回的数组匹配文件大小所以当用这种方式读取一个1.5GB的文件时,你可能很容易就为arrays这几个字节占用了4.5GB。嗯,这不是确切的事实,因为数组大小被限制在大约2GB,所以数组大小不会超过这个,但内部阵列仍为 2GB,新阵列仍为 1.5GB。
请注意,即使在复制到ByteArrayOutputStream
时,分配的 memory 也可能更高。 在最坏的情况下,stream 可能已经拥有一个将近 1.5GB 的数组并且必须增加它,因此它会分配一个 ~2GB 的数组,复制数据,然后它可能会删除旧数组。 所以在增长数组的时候已经临时占用了3.5GB。
您应该重新检查您正在使用的 API,它们是否真的要求您传递一个字节数组,将整个文件保存在堆 memory 中,而不是例如要从中复制的通道或ByteBuffer
,它可以是 memory 映射文件,而不是封装一个大批。
如果那不可能,至少要消除多余的副本。
您可以省略ByteArrayOutputStream
,只使用文件大小的ByteBuffer
,将文件读入其中,并使用其字节数组。
protected boolean uploadFile(File file) throws BaseAFException {
PayloadMessage payload = new PayloadMessage();
try(FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ);
FileLock lock = channel.lock(0, Long.MAX_VALUE, true)) {
long size = channel.size();
if(size > Integer.MAX_VALUE) {
throw new Exception("can't keep " + size + " bytes in an array");
}
ByteBuffer buff = ByteBuffer.allocate((int)size);
do channel.read(buff); while(buff.hasRemaining());
payload.setContent(buff.array());
}
catch( … ) …
// handle upload HERE when resources have been closed
// to have at least one advantage of reading the entire file into memory
}
或者只使用内置功能
PayloadMessage payload = new PayloadMessage();
payload.setContent(Files.readAllBytes(file.toPath()));
虽然这可能不会在阅读时使用锁定。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.