【问题标题】:Java sockets, everything written stays in memoryJava 套接字,所有写入的内容都保留在内存中
【发布时间】:2012-08-10 23:38:51
【问题描述】:

我正在使用这样的 ByteArrayOutputStreams 通过套接字发送图像。

ImageIO.write(image, "gif", byteArrayO);
       byte [] byteArray = byteArrayO.toByteArray();
       Connection.pw.println("" + byteArray.length);
       int old = Connection.client.getSendBufferSize();
       Connection.client.setSendBufferSize(byteArray.length);
       Connection.client.getOutputStream().write(byteArray, 0, byteArray.length);

一切正常,图像最终是130kb,我是这样接收的

int nbrToRead = Integer.parseInt(slave.input.readLine().trim());
            int old = slave.socket.getReceiveBufferSize();
            slave.socket.setReceiveBufferSize(nbrToRead);
            byte[] byteArray = new byte[nbrToRead];
            int nbrRd = 0;
            int nbrLeftToRead = nbrToRead;
            while (nbrLeftToRead > 0) {
                int rd = slave.socket.getInputStream().read(byteArray, nbrRd, nbrLeftToRead);
                if (rd < 0)
                    break;
                nbrRd += rd; 
                nbrLeftToRead -= rd;
            }

            ByteArrayInputStream byteArrayI = new ByteArrayInputStream(byteArray);

            BufferedImage img = ImageIO.read(byteArrayI);

它运行良好,但是发送到 java 内存堆的每个图像都会增加 50 mb。我尝试设置接收缓冲区大小,但它仍然保持不变。它在堆中达到最大值,然后停留一段时间然后停止。

我怎样才能清除缓冲区,以便当它接收到字节时会处理掉它们?

【问题讨论】:

标签: java sockets inputstream outputstream


【解决方案1】:

我想到了几件事:

  • 您可能持有对某个对象的引用,而垃圾收集未将其清理干净。如果做这项工作的类是单例,这是一个很常见的问题。
  • 确保对正在使用的任何 IO 类(如 BufferedInputSteam)调用 close()。

【讨论】:

  • flush()s 比 close()s 更重要。
  • ByteArrayInputStream 没有 flush() 方法。 ETA:但是 flush() 可能对发件人有用……没有足够的关于该代码 sn-p 的信息可以查看。
  • 非常抱歉!我现在正在测试关闭它,我以为它会关闭连接,但它只是关闭了 bytearrayoutputstream 的本地实例。
  • 它在发送图像的一侧泄漏。不是接收者。
【解决方案2】:

确保您的对象在发送后被取消引用,因为剩余的引用会将您的图像保留在内存中。您可以通过将该对象的所有实例设置为 null 来做到这一点。

此外,完成后调用flush()close() 可以极大地帮助您使用内存。

我希望这会有所帮助!

【讨论】:

    【解决方案3】:

    您还没有向我们展示您的所有方法,但在等待下一张图像时,您可能在作用域内拥有大内存局部变量。

    试试这个:

    // after you've finished with the input, dereference the memory-heavy objects...
    byteArray = null;
    byteArrayI = null;
    

    此外,您可以通过调用System.gc() 并让出线程来“推动”JVM 进行垃圾收集(您不能使它进行垃圾收集)。如果单独归零不起作用,请尝试添加此

    System.gc(); // "suggest" garbage collection to execute
    Thread.yield(); // I have seen this encourage garbage collection to execute
    

    【讨论】:

    • 现在我知道问题出在哪里了。几乎完全相同。不确定我是否应该提出一个新问题,所以:当我创建 bytearrayoutputstream 的新实例并使用 ImageIO.write 在那里写入图像时,它首先在内存中上升 100 mb,然后慢慢增加,几次后它有点停止工作。是的,我冲洗、重置然后关闭它。
    • 是的。您必须在发送方close(),否则无法完成发送。请注意,close() 也会自动执行 flush(),因此无需同时调用 flush()
    • 不。我将所有内容都写入此字节流,然后通过套接字“正常”写入。那不是问题。工作正常。但是当我对它执行 ImageIO.write 时,内存会增加。当我停止使用流或关闭它时它不会消失
    【解决方案4】:

    我愿意打赌Connection 持有ObjectOutputStream。如果这是真的,那么您需要在该流上调用 reset()

    【讨论】:

    • 不,我使用 socket.getOutputStream() 和 socket.getInputStream()
    【解决方案5】:

    我发表了评论,但也将其作为答案。

    当您在内存中创建BufferedImages 时,您必须在完成后调用flush()。 我非常怀疑您的内存泄漏就是由此而来。

    而且,正如 Malached 所说,确保所有内容(BufferedImages、I/O 引用等)都不会留下任何类型的引用,以供 gc 使用。

    System.gc()不是解决方案,如果不是你的物理内存真的比你的memory-leak-free程序小,所以你必须在gc自己工作之前尝试一下。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多