【问题标题】:ThreadLocal<ByteArrayOutputStream> causes OutOfMemoryThreadLocal<ByteArrayOutputStream> 导致 OutOfMemory
【发布时间】:2019-02-15 10:21:27
【问题描述】:

当我有这样的课程结构时,我最终会遇到 OutOfMemory 错误。

DataHandler由8个线程的固定池调用(没有外部线程池管理。固定池创建一次,执行一次,如果一个线程死了,就不再创建新线程)。一次,多个线程调用 DataHandler,但由于 byteArrayOutputStreamBuffer 是 threadLocal,每个线程都有自己的本地缓冲区。每个线程获取数据,调用 HandleData(),一旦完成,重复循环。

传递的数据大小为 2 GB。因此,预期占用的总内存最多为(2 GB + 字节数组流的大小)* 线程数。数组流的最大大小应为 4 GB(由于调整内存大小而导致数据的两倍)。因此,预期的总堆将为 6*8 = 48 GB。堆被配置为处理更多(我已经尝试了高达 300 GB),但这个问题仍然存在。

public class DataHandler {
    private static ThreadLocal<ByteArrayOutputStream> byteArrayOutputStreamBuffer =
        new ByteArrayOutputStream();

    void HandleData(byte[] data) {
        ByteArrayOutputStream byteArrayOutputStream = byteArrayOutputStreamBuffer.get();
        File tempFile = new File(getFileName());
        try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
            byteArrayOutputStream.write(data);
            fileOutputStream.write(byteArrayOutputStream.toByteArray());
        } finally {
            byteArrayOutputStream.reset();
        }
    }
}

如果我删除了中间的 ByteArrayOutputStream,那么就没有 OOM。我正在尝试解释为什么 ByteArrayOutputStream 会导致 OOM。

编辑:我看到 toByteArray() 还会增加 2 GB,所以总数为 64 GB。

【问题讨论】:

  • 你有什么邪恶的计算机,它有 300GB 的内存供 JVM 使用?我是否可以建议流式传输数据而不是将整个数据存储在字节数组中?
  • 像往常一样,Threadlocal 是一种代码味道 99.9999999% 的时间,因为这个人不明白它做什么、它是如何做的以及什么时候应该或不应该做的时间百分比使用。这也不是当时的0.00000001%
  • 据我了解,如果线程管理不透明,threadlocal 会很危险。在这里,我只是创建了 8 个线程,它们永远不会重新创建或替换。我做了一个堆转储,我看到这 8 个线程中的每一个都“拥有” byteArrayOutputStreamBuffer 并占用了大量内存。即使 ByteArrayOutputStream 永远不会被垃圾回收,我也不认为它会超出大小这么多,因为我们重用了相同的缓冲区。
  • @user2478398 一些 EC2 实例提供高达 500 GB 的内存。
  • Runtime.getRuntime().maxMemory(); 为您的应用程序返回什么?

标签: java out-of-memory thread-local


【解决方案1】:

ByteArrayOutputStream 最多可容纳Integer.MAX_VALUE - 8 字节(8 字节小于 2Gb),因为它将其数据存储在单个 byte[] 中,并且数组长度限制为该值。

如果你尝试放入更多数据,它会抛出一个OutOfMemomoryError

既然你要输入这么多数据,那可能就是这样。

在这种情况下,您不能使用ByteArrayOutputStream

但是你为什么需要它?为什么不直接将FileOutputStream 存储在 ThreadLocal 中?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 2013-08-25
    • 2011-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多