【问题标题】:How do I pre-allocate memory for an array in PHP?如何在 PHP 中为数组预先分配内存?
【发布时间】:2015-12-05 12:42:34
【问题描述】:

如何在 PHP 中为数组预先分配内存?我想为 351k longs 预先分配空间。该函数在我不使用数组时有效,但如果我尝试在数组中保存长值,那么它会失败。如果我尝试一个简单的测试循环来用range() 填充 351k 值,它就可以工作。我怀疑该数组导致内存碎片,然后内存不足。

在Java中,我可以使用ArrayList al = new ArrayList(351000);

我看到了 array_fillarray_pad,但它们将数组初始化为特定值。


解决办法:

我使用了多种答案。凯文的答案单独工作,但我希望将来随着规模的增长也能防止出现问题。

ini_set('memory_limit','512M');
$foundAdIds = new \SplFixedArray(100000); # google doesn't return deleted ads. must keep track and assume everything else was deleted.
$foundAdIdsIndex = 0;
// $foundAdIds = array();
$result = $gaw->getAds(function ($googleAd) use ($adTemplates, &$foundAdIds, &$foundAdIdsIndex) { // use call back to avoid saving in memory
  if ($foundAdIdsIndex >= $foundAdIds->count()) $foundAdIds->setSize( $foundAdIds->count() * 1.10 ); // grow the array
  $foundAdIds[$foundAdIdsIndex++] = $googleAd->ad->id; # save ids to know which to not set deleted
  // $foundAdIds[] = $googleAd->ad->id;

【问题讨论】:

  • “当我不使用数组时”和“然后它失败”是什么意思?发生什么了?您是否看到任何错误消息?
  • 您是否有不想将数组元素初始化为特定值的原因?
  • “当我不使用数组时,该函数有效” - 我们可以看到函数的代码吗?
  • 你想通过预分配内存来解决什么问题?如果您因致命错误而内存不足,那么 Kevin 下面提出的解决方案就是您问题的答案。确定存储N longs 数组需要多少内存,添加您可能需要的任何额外内存,然后将脚本的memory_limit 设置为足够大的值。

标签: php arrays memory-management


【解决方案1】:

快速回答是:你不能

PHP 与 java 完全不同。

您可以按照您所说的创建具有特定值的数组,但您已经知道它们。你可以通过用空值填充它来“伪造”它,但老实说这差不多。

因此,除非您只想使用 array_fillnull 创建一个(这是我的想法),否则您不能。

(您可能想检查您对内存的推理。您确定这不是 XY 问题吗?由于内存受数字限制(最大使用量),我认为碎片不会有效果很大。检查是什么占用了你的记忆,而不是尝试走这条路)

【讨论】:

  • 是的,我确定,因为我已经通过回调而不是返回对象块使代码变得相当复杂。当我注释掉 ids 数组并且不填充它时,没有内存问题。然而,在单独的测试中,我可以毫无问题地填充该大小的数组。填充整数数组和其他代码的组合导致了问题,但其他代码在一个函数中,该函数以 2000 块为单位调用并超出范围,释放它可能使用的任何内存。跨度>
  • 好吧,我当时不知道,但这变成了一个 XY 问题。事实证明 MySQL 不喜欢 300k+ 项 NOT IN 子句并且实际上锁定了。所以我对网络服务进行了更多研究,发现我可以通过两个单独的调用来返回已删除的项目,一个专门要求删除的项目。
【解决方案2】:

PHP 有一个带有 SplFixedArray 的数组类

$array = new SplFixedArray(3);
$array[1] = 'test1';
$array[0] = 'test2';
$array[2] = 'test3';
foreach ($array as $k => $v) {
    echo "$k => $v\n";
}
$array[] = 'fails';

给予

0 => 测试1

1 => 测试2

2 => 测试3

【讨论】:

  • 不幸的是,我尝试了这个,并尝试在每次达到容量时将数组增长 10%,但它仍然因内存不足错误而失败(在其他地方,不是在调整大小期间)。 if ($foundAdIdsIndex >= $foundAdIds->count()) $foundAdIds->setSize( $foundAdIds->getSize() * 1.10 );
【解决方案3】:

您最接近的是使用SplFixedArray。它不会预先分配存储值所需的内存(因为您无法预先指定使用的值的类型),但它会预先分配数组插槽,并且在添加值时不需要调整数组本身的大小。

【讨论】:

  • 但我猜它仍然不会为内存问题做任何事情?
  • 很难说。目前还不清楚首先是什么问题。也许为数组扩展重新调整内存是真正的问题,而使用 SplFixedArray 的效率稍高一些,足以避免该问题。 OP 可能正在接近那里的极限,所以这可能有所作为。
  • 根据文档,SplFixedArray 提供更快的数组访问。它没有明确说明它的内存效率更高(尽管可能如此)。它也不会增加 php 脚本可以消耗的内存量,这似乎是 OP 的问题,即使 OP 还没有意识到这一点。
  • 我坦率地承认我在这里推测并且 OP 的问题可能完全是另外一回事。但是,如果您知道您将拥有多少个元素,那么无论哪种方式,SplFixedArray 都值得考虑......
  • 不幸的是,它仍然内存不足。不,我不知道总共会有多少元素,只是一个粗略的猜测。我试图动态调整 10% 的大小,但它在其他地方失败并且内存不足。 if ($foundAdIdsIndex >= $foundAdIds->count()) $foundAdIds->setSize( $foundAdIds->count() * 1.10 );
【解决方案4】:

正如其他人所指出的,您不能在 PHP 中执行此操作(好吧,您可以创建一个固定长度的数组,但这并不是您真正需要的)。但是,您可以做的是增加进程的内存量。

ini_set('memory_limit', '1024M');

把它放在你的 PHP 脚本的顶部,你应该没问题。您也可以在php.ini 文件中进行设置。这不会为 PHP 分配 1GB 内存,而是允许 PHP 扩展它的内存使用量。

有几点需要指出:

  • 在某些共享主机上可能不允许这样做
  • 如果您正在使用这么多内存,您可能需要看看您是如何做事的,看看它们是否可以更有效地完成
  • 使用unset($x);寻找机会清除不需要的资源(您真的需要保留包含您已经使用过的巨大对象的$x吗?)

【讨论】:

  • Heroku 允许 ini_set,但它们的 dyno (VPS) 限制为 512M。我只会增加memory_limit。我已经使用了回调函数来避免返回 ActiveRecord/DBO 对象的列表,并一次处理一个。但是,我使用的 Web 服务不会返回已删除的项目,因此我必须跟踪哪些 ARE 返回并将 REST 标记为已删除。
猜你喜欢
  • 2016-05-31
  • 1970-01-01
  • 2019-05-18
  • 2016-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-18
  • 1970-01-01
相关资源
最近更新 更多