【问题标题】:Java consecutive memory allocation for dynamically growing array?动态增长数组的Java连续内存分配?
【发布时间】:2013-08-13 04:06:27
【问题描述】:

我正在尝试在 java 中实现一个面向列的数据存储引擎。我想知道是否有任何其他方法可以为动态增长的数组实现连续内存分配。

HashMaps 无法在扩展/调整大小时分配连续的内存块。


即使通过创建更大大小的新固定数组并将值从旧固定数组复制到这个新数组,看起来也是实现连续性的唯一选择,但与 for ex 相比,这非常慢。假设您在当前大小为 100 万的列(固定数组)中已有 100 万条记录,您需要在 1000001 位置插入新值,然后 jvm 必须创建大小为 1000001 的新数组并将所有值复制到新的更大尺寸的数组(仅插入一个值)并保持连续性。


ArrayList 在内部的工作方式与上述相同(分配新数组 + 复制旧值等)。因此,作为线程安全的具有额外同步开销的向量。


因此,通过在初始化期间创建一个巨大的固定数组来分配大量连续内存的另一种方法会导致大量未使用的内存,这不是一个可行的解决方案。


如果有更好的选择,请提供帮助。例如。类似(如果可以在 Java 中实现)知道当前固定数组中最后一个元素的地址并以某种方式检查下一个连续可用块是否可用?如果是这样,那么使用它来存储新值以及更新数组索引以适应此新更改以维持 O(1) 时间读取访问?


谢谢。

【问题讨论】:

  • 嗯,这些基本上是您的选择:分配很多开始并知道您不必复制但同时“浪费”内存,或者分配更小的块并根据需要进行复制。大小合理的块的链表是一个“中间地带”。
  • 为什么连续记忆对你很重要?此外,ArrayList 由数组支持,因此是连续的内存。当然,你也可以像ArrayList那样手工做同样的事情(过程中避免一些拳击),真的没那么难。
  • 我刚刚检查了 ArrayList 的 jdk 内部实现,并知道内部它被初始化为默认大小 10,然后执行创建新数组 + 1 大小的相同过程 -> 将旧值复制到这个新的,当它需要扩展 add() 方法时。所以我认为它们分配有连续的内存块(由数组索引支持),但在重新调整巨大数组列表的大小时会影响性能。
  • 那么还有其他解决方案吗?例如。知道当前固定数组中最后一个元素的地址,并以某种方式检查下一个连续可用块是否可用?如果是这样,那么使用它来存储新值以及更新数组索引以适应此新更改以维持 O(1) 时间读取访问?
  • 你真的做过基准测试吗?我强烈建议您高估了调整大ArrayLists 的“性能影响”。 (对于初学者来说,add 摊销 O(1) 甚至考虑到调整大小。)

标签: java memory-management jvm dynamic-arrays


【解决方案1】:

如果您尝试“手动”执行此操作,一种常见的技术是每次需要增加数组的大小时将其加倍。因此,在您的示例中,您可以将数组的大小调整为 200 万;这很昂贵,但这意味着您在很长一段时间内都不需要再次调整大小。

这使您可以在 摊销 恒定时间内插入数组,尽管有时可能不希望进行像复制 100 万行这样的昂贵操作,因此您可能必须修改此想法以适应您的特定需求.有关动态数组实现的更多讨论,请参阅http://en.wikipedia.org/wiki/Dynamic_array

【讨论】:

  • 这是在每次重新调整大小时分配更多内存的好方法,但随后会创建大量未使用的空间,jvm 无法将其用于其他目的。增加像 2^x , x=1 到 n 这样的大小将是一个好方法。仍然需要更令人信服的解决方案或具有 O(1) 读取访问时间的数据结构,如数组中。
【解决方案2】:

有很多技巧,但 Java 的 ArrayList 是现有的最有效的可增长数组组合之一。

您可以创建具有固定长度的数组,然后将它们连接到一个列表中(因此增长只需要附加一个额外的数组而不需要复制它)。但是,如果您的数据结构预计会增长很多,最好将其完全实现为列表。

您可以通过将连接的数组的大小加倍来扩展它。因此,您创建了一个数组列表,其大小分别为 50, 100, 200, 400 等等。您可以按如下方式计算数组(和位置):

int x = 55; // position

int position = (int)Math.floor(Math.log(1 + x / 50) / Math.log(2));
int arrayposition = x - (Math.pow(2, position) * 50);

即使对于大数据值,这仍然是一个相当快的数据结构(O(n) 是数据检索的最坏情况值,扩展它是 O(1)

【讨论】:

  • 拥有多个固定大小的数组并将它们连接到一个列表中的解决方案看起来比纯 ArrayList 更好,但仍不能保证连续分配,我创建另一个固定数组以附加到先前现有的数组列表。是否有可能获得完全的连续性?
  • 如果你想要完整的连续性,你将不得不分配它。如果内存没有分配,系统可以将其用于其他目的。如果你需要空间,那么你必须指出它,所以它会被保留。如果需要扩展又不能准确预测大小,需要使用列表(即使用内存中不同的块)
  • 我刚刚检查了 ArrayList 的 jdk 内部实现,并了解到内部它被初始化为默认大小 10,然后执行创建新数组+1 大小的相同过程->复制旧值等。当它需要扩展 add() 方法时。所以我认为它们在连续的内存块内,但在调整大小时会影响性能。
  • 此算法在调整大小时不会影响性能,但在检索值时无法保证O(1) 的性能。
  • 您确定不能保证 O(1) 吗?如果可能的话,我可以指向提到它的文档吗?我也在寻找类似的东西。知道当前固定数组中最后一个元素的地址,并以某种方式检查下一个连续可用块是否可用?如果是这样,那么使用它来存储新值以及更新数组索引以适应此新更改以维持 O(1) 时间读取访问?
猜你喜欢
  • 2021-02-09
  • 2011-01-15
  • 2012-11-12
  • 2014-09-29
  • 1970-01-01
  • 2021-12-02
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
相关资源
最近更新 更多