【问题标题】:Can a JVM array be realloc()'d?JVM 数组可以重新分配()吗?
【发布时间】:2019-10-11 20:21:39
【问题描述】:

因此,C 语言有一个realloc() 过程,可用于就地增加分配的[编辑]内存

JVM/Java 是否存在等效概念?对于Arrays 还是nio.Buffers?我不介意这是 JDK 的一部分还是作为外部库,例如sun.unsafe.

【问题讨论】:

  • AFAIK,realloc 分配一个新的内存块,并将内容复制到那里,然后释放原始的(尽管它声称如果碰巧没有内存,它可能会增长它在它背后......无疑是一个罕见的案例)。 ArrayList 正是这样做的,但它是透明的(即,它会在需要时自动增长)。
  • @john16384 通过System::arrayCopy 在内部复制数组;但在其他方面完全正确
  • 我对扩展现有块的能力特别感兴趣,而不是“复制它”。我在哪里可以找到这种效果的证据@john16384,你说“无疑是一个罕见的案例”——你的依据是什么?也许这个问题的标题应该是“是否有可能在 JVM 堆中增加现有分配”以使这一点更清楚。
  • JVM 可能会在内部执行类似的操作,但是从您的 Java 代码中,您无法以任何方式控制它。你不能拿byte[] 要求更大的,你只能创建一个新的。至于我声称在 C 中增长数组是一种罕见的情况——内存会碎片化,如果你为你的 C 程序编写自己的分配策略,你可以对此进行一些控制,但是,随着许多分配的发生,正确的机会尚未使用的区域很小。

标签: java jvm realloc


【解决方案1】:

首先,C 函数realloc 不是可以增长数组 的工具,而是以前allocated via malloc, calloc, or realloc 的内存。相比之下,C 数组可能驻留在堆栈或静态内存区域中,甚至可能嵌入到更大的结构中。

然后,该函数具有像free 一样使原始指针无效并返回新指针的语义。所以如果你有指针的副本飞来飞去,你有责任用新的指针替换所有出现的地方。

当然,Java 不支持使引用无效。保证每个非null 对象引用的有效性,是Java 内存管理的基本属性。

因此,如果您想要与 C 的 realloc 等效,则必须使用指针而不是数组。那么sun.misc.Unsafe就有了所有相关的操作

  • public long allocateMemory(long bytes)
  • public long reallocateMemory(long address, long bytes)
  • public void freeMemory(long address)
  • public float getXyz(long address)¹
  • public void putXyz(long address, xyz x)¹

¹其中“xyz”代表原始类型

如果要调整数组大小,请使用

array = Arrays.copyOf(array, newSize);

这不会使旧引用无效,因此如果您未能替换对旧数组的所有引用,则使用这些旧引用的代码将访问旧数组。

但是,如果 array 是对特定数组的唯一引用,或者您的代码已被证明替换了所有现有引用,则 JVM 理论上可以启用就地调整大小操作而不是复制内容,另请参阅 Does Java JIT cheat when running JDK code?

但是检查必要的先决条件可能比复制数组内容更昂贵。我可以想象适用性的唯一场景是,如果数组是同一线程分配的最后一个对象,因此可以证明引用尚未转义,并且分配空间内的数组后面没有对象。

对于具有这种优化的 JVM,您可能会在重复添加到 ArrayList 时自动获得好处,因为该类确保它是对其内部数组的引用的唯一持有者,并且它在内部使用 Arrays.copyOf用于调整大小。

【讨论】:

  • 我已经根据您的 cmets 调整了 OP,以反映 realloc 不对数组进行操作。感谢您的全面回答。
猜你喜欢
  • 2017-09-02
  • 2018-07-30
  • 1970-01-01
  • 2019-07-19
  • 2020-09-05
  • 1970-01-01
  • 2013-09-09
  • 1970-01-01
  • 2020-04-10
相关资源
最近更新 更多