简体   繁体   English

GZIPInputStream 内存泄漏

[英]GZIPInputStream memory leakage

I am getting a java.lang.OutOfMemoryError: Java heap space when using GZIPInputStream.使用 GZIPInputStream 时出现 java.lang.OutOfMemoryError: Java heap space。 The Java process runs good for some time but after a while full ups the memory. Java 进程在一段时间内运行良好,但一段时间后内存已满。 I guess there is some references that are not taken care by the GC but can really find where the problem in the code could be.我猜有一些引用没有被 GC 处理,但可以真正找到代码中的问题所在。 I already increase the memory of the process to 3 GB but for sure after a while will full up that memory too.我已经将进程的内存增加到 3 GB,但肯定会在一段时间后填满该内存。 Is really progressive and no matter the memory size.真的是进步的,无论内存大小。 Does any one have an idea how I could improve my code to prevent memory leakage?有没有人知道如何改进我的代码以防止内存泄漏?

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) {
    }
}

Is reasonable to assume the inflated stream is bigger than msg so your can help your program use fewer re-allocations if you use new ByteArrayOutputStream(msg.length) .假设膨胀的流大于msg是合理的,因此如果您使用new ByteArrayOutputStream(msg.length)则可以帮助您的程序使用更少的重新分配。 Also, get rid of the double buffering by removing BufferedInputStream and your own buffer, just call transferTo which allocates a single internal buffer.此外,通过删除 BufferedInputStream 和您自己的缓冲区来摆脱双缓冲,只需调用transferTo分配单个内部缓冲区。

Thus your program will do fewer memory re-allocations if you reduce it to this:因此,如果您将其减少为以下内容,您的程序将执行更少的内存重新分配:

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();
    }
}

However you may still get OOM - as you have may have 3 big byte[] in memory.但是,您仍然可能会遇到 OOM - 因为您的内存中可能有 3 个大byte[] You may be able use a bigger guess size for the anticipated final length inside the ByteArrayOutputStream to ensure it does not re-allocate when inflating - example: new ByteArrayOutputStream(msg.length * 4 / 3) .您可以对ByteArrayOutputStream内的预期最终长度使用更大的猜测大小,以确保它在膨胀时不会重新分配 - 例如: new ByteArrayOutputStream(msg.length * 4 / 3) If OOM happens at toByteArray() it is also possible to read the internal byte[] with a sub-class.如果在toByteArray()发生 OOM,也可以使用子类读取内部byte[]

Better still, change the structure of your application to avoid full byte[] copies of stream, and change the compression to support caller supplied OutputStream.更好的是,更改应用程序的结构以避免流的完整byte[]副本,并更改​​压缩以支持调用者提供的 OutputStream。

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

or或者

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

I've seen a few things floating (eg example ) around talking about Java OOM caused by zlib off-heap memory allocation which may mean you are out of luck with conventional code.我已经看到一些关于由 zlib 堆外内存分配引起的 Java OOM 的浮动(例如示例),这可能意味着您对传统代码不走运。

In case it is just your code, you should monitor the live heap (try JMC ), take a heap dump (or two) and check the content ( Eclipse MAT lets you diff two heaps).如果它只是您的代码,您应该监视实时堆(尝试JMC ),进行堆转储(或两次)并检查内容( Eclipse MAT可让您区分两个堆)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM