【问题标题】:Java: reliably allocate large array on heapJava:在堆上可靠地分配大数组
【发布时间】:2021-09-28 00:03:27
【问题描述】:

任务

分配 X=4..8MB 的字节数组(在堆上),例如使用 ByteBuffer.allocate() 这样它就不会导致 OutOfMemoryError。不允许拆分阵列并以较小的部分对其进行处理。请注意,分配发生在堆上,这不是直接的 ByteBuffer。

挑战

问题

Java 中有没有办法编写如下代码?

if (<I can reliably allocate an array sized X bytes on heap right now>) {
     ByteBuffer.allocate(X);
}

【问题讨论】:

  • 不,没有。

标签: java garbage-collection out-of-memory heap-memory


【解决方案1】:

不,在 Java 中没有可靠的方法来做到这一点。

有几种方法可以对可用内存进行估计或尽力猜测,但没有一种可靠的方法。另请注意,即使有这样的事情,另一个线程也可以更改条件和调用分配之间的可用数量。

这个related answer 包含一种获得这种估计的方法,并且还解释了一些不可靠的原因。

【讨论】:

    【解决方案2】:

    这个想法存在一个根本问题

    if (<I can reliably allocate an array sized X bytes on heap right now>) {
        ByteBuffer.allocate(X);
    }
    

    称为“check-then-act”反模式。无论if的条件中的检查应该如何工作,您都需要确保它在检查和后续操作(即分配)之间不会发生变化。

    为确保结果不改变,您不仅需要停止同一 JVM 的所有其他线程执行分配(或并发垃圾收集完成),还需要阻止同一台机器的所有其他进程分配内存,因为操作系统可能没有专门为您的 JVM 保留内存,但仍允许其他处理在此时正确使用它。

    条件本身具有您的问题中已经提到的挑战,正如您自己所说,当 JVM 能够即时重新配置它们时,所有这些对实现特定内存区域的摆弄可能没有实际意义。由于这通常是作为对垃圾回收结果的响应来完成的,因此您需要先执行完整的垃圾回收,以确定结果情况。只有在这种情况下,如果我们能够阻止所有其他线程和进程进行分配,我们才能确保另一次 GC 不会改变这种情况。

    在某些 JVM 上,可靠地触发垃圾收集的唯一方法是执行实际分配。

    因此,您需要一种以原子方式执行检查的方法,然后进行实际分配,以确保无论环境中发生什么或内存不可用的答案,您都可以使用内存。这种机制确实存在。只需调用ByteBuffer.allocate(X),如果它正常完成,返回的引用确保内存保持可用,只要你保留它。否则,抛出的OutOfMemoryError 表示内存不可用。由于存在这种机制,因此没有理由提供第二个具有相同结果的机制。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-05
      • 1970-01-01
      • 2014-06-10
      • 2021-05-10
      • 2015-08-21
      • 1970-01-01
      • 2019-01-09
      • 2020-01-05
      相关资源
      最近更新 更多