GZIPInputStream内存泄漏



使用GZIPInputStream时,我得到一个java.lang.OutOfMemoryError:java堆空间。Java进程在一段时间内运行良好,但过了一段时间就会耗尽内存。我想有些引用GC没有注意到,但确实可以找到代码中的问题所在。我已经将进程的内存增加到了3 GB,但过一段时间肯定也会耗尽内存。真的很进步,不管内存大小。有人知道我如何改进代码以防止内存泄漏吗?

public byte[] uncompress(byte[] msg) {
byte[] buffer = new byte[4 * 1024];
int length;
try (GZIPInputStream gzis = new GZIPInputStream(new ByteArrayInputStream(msg));
BufferedInputStream bis = new BufferedInputStream(gzis);
ByteArrayOutputStream baos = new ByteArrayOutputStream()
) {
while ((length = bis.read(buffer)) >= 0) {
baos.write(buffer, 0, length);
}
final byte[] result = baos.toByteArray();
return result;
} catch (Exception e) {
}
}

我看到了一些关于zlib堆外内存分配导致的Java OOM的浮动(例如示例(,这可能意味着你对传统代码不太满意。

如果它只是您的代码,您应该监视活动堆(尝试JMC(,进行一个(或两个(堆转储并检查内容(Eclipse MAT允许您区分两个堆(。

假设膨胀的流大于msg是合理的,因此如果使用new ByteArrayOutputStream(msg.length),您可以帮助程序使用更少的重新分配。此外,通过删除BufferedInputStream和您自己的缓冲区来消除双重缓冲,只需调用分配单个内部缓冲区的transferTo即可。

因此,如果你把它减少到,你的程序将进行更少的内存重新分配

public byte[] uncompress(byte[] msg) throws IOException {
try (GZIPInputStream gzis = new GZIPInputStream(new ByteArrayInputStream(msg));
ByteArrayOutputStream baos = new ByteArrayOutputStream(msg.length)
) {
gzis.transferTo(baos);
return baos.toByteArray();
}
}

然而,你可能仍然会得到OOM——因为你的内存中可能有3个大的byte[]。您可以对ByteArrayOutputStream内部的预期最终长度使用更大的猜测大小,以确保它在充气时不会重新分配——例如:new ByteArrayOutputStream(msg.length * 4 / 3)。如果OOM发生在toByteArray(),则也可以使用子类读取内部byte[]

更好的是,更改应用程序的结构以避免流的完整byte[]副本,并更改压缩以支持调用方提供的OutputStream。

public void uncompress(byte[] msg, OutputStream out) throws IOException

public void  uncompress(InputStream msg, OutputStream out) throws IOException

最新更新