【问题标题】:PHP & do/while loop memory leakingPHP & do/while 循环内存泄漏
【发布时间】:2011-03-13 18:01:09
【问题描述】:

我有一个遍历数据库行的 do/while 循环。因为它在处理 100000 行时运行了很多天,所以要控制内存消耗很重要,否则它会崩溃。现在每次迭代都会增加大约 4kb 到脚本的内存使用量。我正在使用 memory_get_usage() 来监控使用情况。

我在每次迭代中首先取消设置循环中使用的每个变量,所以我真的不知道我还能做什么。我的猜测是 do/while 每次迭代都会收集一些数据,这就是消耗 4kb 内存的原因。我知道 4kb 听起来并不多,但是当您有 100000 次迭代时,它很快就会开始累加。

有人可以提出另一种处理大量数据库行的方法或如何以某种方式消除这种“内存泄漏”吗?

编辑 这是更新的循环代码。上面只有几个 require_once()s。

$URLs = new URLs_url(db());
$c = new Curl;
$c->headers = 1;
$c->timeout = 60;
$c->getinfo = true;
$c->follow = 0;
$c->save_cookies = false;

do {
    // Get url that hasn't been checked for a week
    $urls = null;

    // Check week old
    $urls = $URLs->all($where)->limit(10);

    foreach($urls as $url) {
        #echo date("d/m/Y h:i").' | Checking '.$url->url.' | db http_code: '.$url->http_code;

        // Get http code    
        $c->url = $url->url;
        $data = $c->get();

        #echo ' - new http_code: '.$data['http_code'];

        // Save info
        $url->http_code = $data['http_code'];
        $url->lastchecked = time();
        $URLs->save($url);
        $url = null;
        #unset($c);
        $data = null;
        #echo "\n".memory_get_usage().' | ';
        echo "\nInner loop memory usage: ".memory_get_usage();
    }
    echo "\nOuter loop memory usage: ".memory_get_usage();

} while($urls);

一些记录两个循环中内存消耗的行为:

Inner loop memory usage: 611080
Inner loop memory usage: 612452
Inner loop memory usage: 613788
Inner loop memory usage: 615124
Inner loop memory usage: 616460
Inner loop memory usage: 617796
Inner loop memory usage: 619132
Inner loop memory usage: 620500
Inner loop memory usage: 621836
Inner loop memory usage: 623172
Outer loop memory usage: 545240
Inner loop memory usage: 630680
Inner loop memory usage: 632016
Inner loop memory usage: 633352
Inner loop memory usage: 634688
Inner loop memory usage: 636088
Inner loop memory usage: 637424
Inner loop memory usage: 638760
Inner loop memory usage: 640096
Inner loop memory usage: 641432
Inner loop memory usage: 642768
Outer loop memory usage: 556392
Inner loop memory usage: 640416
Inner loop memory usage: 641752
Inner loop memory usage: 643088
Inner loop memory usage: 644424
Inner loop memory usage: 645760
Inner loop memory usage: 647096
Inner loop memory usage: 648432
Inner loop memory usage: 649768
Inner loop memory usage: 651104
Inner loop memory usage: 652568
Outer loop memory usage: 567608
Inner loop memory usage: 645924
Inner loop memory usage: 647260
Inner loop memory usage: 648596
Inner loop memory usage: 649932
Inner loop memory usage: 651268
Inner loop memory usage: 652604
Inner loop memory usage: 653940
Inner loop memory usage: 655276
Inner loop memory usage: 656624
Inner loop memory usage: 657960
Outer loop memory usage: 578732

【问题讨论】:

  • 循环通常不会泄漏内存,循环中的东西会,向我们展示一些代码,以便我们帮助诊断真正的问题。
  • 这里是代码。希望对您有所帮助。
  • 代码再次更新以匹配当前代码。

标签: php mysql loops


【解决方案1】:

这个位应该只发生一次,在循环之前:

$c = new Curl;
$c->headers = 1;
$c->timeout = 60;
...
$c->getinfo = true;
$c->follow = 0;
$c->save_cookies = false;

编辑:哦,整个事情都包裹在一个 do/while 循环中。 /掌脸

编辑 2:还有一点很重要:

unset($class_object) 不释放 对象分配的资源。如果 在循环中使用,它创建和 销毁对象,这可能很容易 导致资源问题。明确地 调用析构函数来规避 问题。

http://www.php.net/manual/en/function.unset.php#98692

编辑 3:

这是什么?这不能以某种方式移出循环吗?

$URLs = new URLs_url(db());

编辑 4:

现在尝试删除这些行。

    $url->http_code = $data['http_code'];
    $url->lastchecked = time();
    $URLs->save($url);

【讨论】:

  • 这是一个 do/while 循环。 Foreach 循环只是为了批处理。我也不能真正在循环之前做 Curl,因为 foreach 的每次迭代都会检查 10 个不同的 url。
  • @James D52 但是您可以在之前配置 curl 而不是在每次迭代时都创建一个新实例。这些值在迭代之间会发生变化吗?不,他们似乎没有。 URL 会有所不同,来自该 URL 的响应也会有所不同。但是,这并不一定需要每次迭代都需要一个新的 curl 实例。
  • 好的,我在做之前把其他的都移到了开头。现在每个循环中只有 $c->url = $url->url & $c->get() 。没有改变一件事。我仍在某处泄漏〜1.5kb:/
  • @James D52 您如何确定您正在“泄漏”该内存?编辑:另外,如果您关心内存泄漏(可能还有性能),为什么在不需要变量插值时使用双引号字符串?
  • 好吧,“泄漏”只是我用的一个词。它在某处为不必要的东西保留了~1.5kb。所以你可以说我的代码“泄漏”了 :) 我会写一些 __destruct 方法看看是否有帮助。
【解决方案2】:

我认为您的核心问题是您只是在清除外部循环中的内容。

$c = new Curl 例如将为内部循环的每次迭代分配内存给堆,但你只是unseting 最后一个实例。我想unset 在内循环的end 处提供任何你能做到的东西($c$data)。

【讨论】:

  • 通过将 unsets 放在最后,我设法将内存泄漏从 4kb 减少到大约 1.5kb。我想知道什么还在泄漏:/
  • @James,将调用移至memory_get_usage()unset($URLs) 之后,我感觉你会发现1.5kb 的增长与$URLs->save() 调用有关。
  • 什么也没做。每次迭代时,内存消耗仍会增加约 1.5kb。
  • @James,db() 是做什么的?当您说迭代时,您是指内部循环还是外部循环迭代?
  • db() 连接到 MySQL。我正在使用 phpDataMapper 类。顺便说一句很棒的课:) $URLs = new URLs_url(db());现在已从两个循环中移出,因此它只运行一次。
【解决方案3】:

问题大概是

$c = new Curl

是否可以在循环外实例化 Curl,然后在循环内部继续重用同一个实例。如果需要,您可以在循环中将所有字段重置为 null。

我遇到了类似的问题。 Unset 没有用——结果证明垃圾收集是垃圾。当我重用对象时,一切都很好(好吧,由于不同的原因它坏了,所以我最终用 Java 重新实现了)。

【讨论】:

【解决方案4】:

这可能对您有所帮助,也可能无济于事,但早在 2000 年,我有一个客户的互联网速度非常慢,他想在本地完成他所有的网站 cms 更新,并在完成后更新。那时在 win xp 上的 IIS 上,我找不到将脚本超时时间从 60 秒增加的方法,并且通常需要 2 分钟来完成更新,所以它显然会超时。

为了解决这个问题,我会让脚本更新一组保证在一分钟内安全执行的行,然后使用从哪里继续的参数调用自身,依此类推,直到所有行都被更新。也许您可以根据自己的情况尝试类似的方法?

也许在调用自己之前运行它一段时间,或者在你的情况下,也许检查内存,并在使用率过高时重定向?

我用过这样的东西:

脚本顶部:

$started = microtime(true);

然后在你的循环中:

if((microtime(true)-$started) > ($seconds_to_redirect)) {
    //call script with parameter
}

我能想到的就这些了。

【讨论】:

    猜你喜欢
    • 2014-12-28
    • 1970-01-01
    • 2016-02-21
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多