【问题标题】:Issue of memory exceed when update array values in loop in phpphp中循环更新数组值时内存超出问题
【发布时间】:2015-11-18 15:57:54
【问题描述】:

情况是我有一个包含 15000 条记录的数组,每条记录有 67 个键值对值。现在我想使用 trim、str_replace 或 type-cast 更新所有值,但我得到一个 php 内存泄漏的错误。我不明白为什么会这样。我的数组格式是这样的

Array
(
    [0] => Array
        (
            [id] => 1
            [parent_id] => 1
            [login] => 123
            [login_nickname] => 
            [password] => 123
            [subuser] => 0
            [subuser_id] => 
            [email] => usersemail@email.com
        ),
       [1] => Array
        (
            [id] => 1
            [parent_id] => 1
            [login] => 123
            [login_nickname] => 
            [password] => 123
            [subuser] => 0
            [subuser_id] => 
            [email] => anotheruser@email.com
        )
)

在这个数组中,我有一些字段,如 cmets、地址,我希望它们修剪、str_replace,一些字段如分支(数组)我想在对象中进行类型转换。但是当我这样做时,我会收到这样的内存超出错误

致命错误: var/www/html/myproject/application/models/Member.php 中允许的内存大小为 134217728 字节已用尽(尝试分配 49 字节)上线28

仅供参考:当我使用这些函数(trim、str_replace、type-cast 等)在循环中回显这些值时,循环工作正常并成功打印其值。

我不明白是什么问题,请帮助我。

我的 foreach 循环是这样的

foreach($member_list as $index=>$member){
   foreach($member as $key=>$value){
      $member_list[$index][$key] = str_replace(array("\n", "\t", "\r"), " ", trim($value));
   }
}

这个错误发生在这个循环中。

【问题讨论】:

  • 您需要出示您的代码。
  • 它来自数据库吗?把它分块
  • 您可以使用 memory_get_usage 来查看构建阵列时的使用情况 (php.net/manual/en/function.memory-get-usage.php)。
  • @Dagon,是的,这个数组来自数据库,但在这个问题中,我只是展示了我的数组的格式。
  • @Barmar,我添加了一部分代码

标签: php loops memory-leaks variable-declaration


【解决方案1】:

这个问题基本上与PHP脚本消耗大量内存有关。在您的情况下,由于数据量太大,即使是 504 网关超时问题也会开始频繁出现。您应该使用批处理(后台处理)技术或使用 CLI 或 curl 执行脚本。这两种技术是 PHP 世界中处理大型运行作业的成熟方法。

【讨论】:

  • 你能提供任何可以解决我的目的的例子吗?
【解决方案2】:

问题是 str_replace 实际上是一种相当昂贵的使用方法。如果你分析你的代码(使用像 xhprof (http://pecl.php.net/package/xhprof) 这样的工具,你会看到这种方法在 PHP CPU 使用方面真的是多么昂贵。最重要的是(我认为这是你真正的问题),如您正在循环并分配值,您正在加倍内存,这是您最有可能遇到问题的地方(回显值时您看不到的问题)。

如果您仍需要像这样运行循环,请增加内存限制。您可以在 php.ini 文件中执行此操作(然后重新启动 Apache),也可以在脚本的开头放置以下代码:

ini_set('memory_limit','256M')

这将使 PHP 的可用内存增加到 256MB。您可能需要添加更多,但最终,您将拥有足够的资源来处理整个数组。

另一方面,请考虑修复进入数据库的数据,当然,除非您这样做只是出于特定目的。

---只是用内存使用的例子来更新这个---

示例 1:

http://sb.meezaan.net/so/32195660/t1.php 上运行以下代码使用大约 43,000 个字节。

<?php
$x = [];
// create array.
$i = 0;
for($i=0; $i<=1000; $i++) {
    $x[$i]['id'] = $i;
    $x[$i]['name'] = rand(10909090,999999999999999);
    $x[$i]['time'] = time();
    $x[$i]['readable'] = date('r', time()); 
}

$start = memory_get_usage();
foreach ($x as $k => $v) {
    foreach ($v as $key => $val) {
        $x[$k][$key] = str_replace(1,2, $val);
    }
}
$end = memory_get_usage();
$used = $end - $start;

var_dump('Memory consumed: ' . $used);

示例 2:

http://sb.meezaan.net/so/32195660/t2.php 上运行以下代码使用大约 770 个字节(滚动到底部查看结果)。

<?php
$x = [];
// create array.
$i = 0;
for($i=0; $i<=1000; $i++) {
    $x[$i]['id'] = $i;
    $x[$i]['name'] = rand(10909090,999999999999999);
    $x[$i]['time'] = time();
    $x[$i]['readable'] = date('r', time()); 
}

$start = memory_get_usage();
foreach ($x as $k => $v) {
    foreach ($v as $key => $val) {
        echo str_replace(1,2, $val) . '<br />';
    }
    echo '---<br />';
}
$end = memory_get_usage();
$used = $end - $start;

var_dump('Memory consumed: ' .  $used);

【讨论】:

  • 为什么内存会加倍?每次他替换一个字符串,旧的字符串就会变成垃圾,应该回收它的内存。 PHP 使用写时复制,因此分配变量不会复制,除非您修改其中一个。
  • 我不确定 PHP 背后的 C 代码是如何工作的,但根据我的经验,只有在脚本终止后才会回收内存。当他分配给变量时,他的内存用完了 - 当他回显时他没有 - 这意味着他正在消耗内存。问题很简单。我会尝试运行一些测试,看看我是否可以产生一些统计数据。
  • 回显时的代码参见sb.meezaan.me/so/32195660/t2.phpsb.meezaan.me/so/32195660/t2.txt。在转储结束时查找内存使用情况。请参阅sb.meezaan.me/so/32195660/t1.phpsb.meezaan.me/so/32195660/t1.txt 进行赋值时的比较。 t1 示例的内存使用率要高得多,超过 55 倍。
  • 查看php.net/manual/en/features.gc.php了解PHP垃圾回收的详细信息。
  • 会的。然而,证据就在布丁中(或上面的例子)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-10
相关资源
最近更新 更多