如何使用大流数据防止Out of Memory错误?

时间:2017-10-12 18:10:50

标签: java

我正在处理未知大小的输入流,我需要将其序列化为byte []以实现故障安全行为。

我现在有基于IOUtils的代码,但是有5-50个不同的线程可能运行这个,我不知道它有多可靠。

try(final ByteArrayOutputStream output= new ByteArrayOutputStream()){
long free_memory = Runtime.getRuntime().freeMemory() / 5;
final byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
    output.write(buffer, 0, n);
    count += n;
    free_memory -= n;
    if (free_memory < DEFAULT_BUFFER_SIZE) {
        free_memory = Runtime.getRuntime().freeMemory();
        if (free_memory < (DEFAULT_BUFFER_SIZE * 10)) {
            throw new IOException("JVM is low on Memory.");
        }
    free_memory = free_memory / 5;
    }
}
output.flush();
return output.toByteArray();
}

我想在问题出现之前捕获OOM错误并杀死该线程,并且我不想将该流保存为文件。有没有更好的方法来确保你不会使用太多的记忆?

(我使用的是Java 8)

1 个答案:

答案 0 :(得分:0)

如果多个线程正在运行相同的代码,那么太过回答你的问题,这是一种非常不可靠的方式。

代码询问系统有多少内存可用Runtime.getRuntime().freeMemory(),这是一个在它返回的瞬间过时的值,因为其他线程在此期间会消耗更多的内存。在抛出一些不那么明显的剩余内存阈值的情况下应该抛出的相应I / O异常可能会也可能不会执行,但是它是否完全不重要。

数据的捕获是在ByteArrayOutputStream内完成的,每次到达结束时都会增加(并复制)其缓冲区。它不受“有多少内存”检查的控制,因此多个线程将同时调整其缓冲区大小,其中任何一个都可能失败。

最安全的方法是将数据存储在磁盘上,从而制作副本。如果数据来自外部流媒体源,您可以使用      Files.copy()。如果您获得的是文件,则可以使用copy的其他变体,我认为将其委托给操作系统。