每次循环后内存使用量增加

6

我有一个foreach循环,正在读取包含大约200000行的文件,这是我的PHP代码:

foreach ($this->file as $row)
      {
        if ($this->file->valid())
        {

          //init client array
          $this->initInvoicesArray($row);
          $this->prepareInvoice();
          $row = null;
          $this->key = $this->file->key();

          //add msisdn to msisdn array, and client to clients array
          self::$MsisdnArray[] = $this->msisdn;
          self::$InvoicesArray[$this->msisdn] = $this->client;

          if ($i % 3000 == 0) 
          {
            //get valid users from table
            $this->prepareAndSaveValidClients();
          }

          $i++;
        }
      }

以下是prepareAndSaveValidClients()函数:

public function prepareAndSaveValidClients(){
    $query = Doctrine_Query::create()
        ->select('p.gender, p.email2, u.username, u.first_name, u.last_name, u.email_address, u.is_active, p.msisdn, p.user_id, p.city_id, p.street, p.zipcode, p.msisdn_status')
        ->from('sfGuardUser u')
        ->innerJoin('u.Profile as p ON u.id = p.user_id')
        ->whereIn('p.msisdn', self::$MsisdnArray)
        ->whereIn('p.status', self::$AllowedStatus);

    $results = $query->fetchArray();

    //instanciat an object collection for payment_notifications
    $collection = new Doctrine_Collection("payment_notifications");

    if (!empty($results))
    {
      foreach ($results as $key => $client)
      {
        $invoice = self::$InvoicesArray[$client['Profile']['msisdn']];

        $this->initInvoicesArray($invoice);
        $this->prepareInvoice();

        $this->prepareUserProfile($client);
        $this->prepareClient();

        $paymentNotifications = new paymentNotifications();
        $paymentNotifications->fromArray($this->client);

        $collection->add($paymentNotifications);

        $tel = $client['Profile']['msisdn'];
        $client = null;
      }

      $collection->save();

      //clear memory
      $results = null;
      $collection = null;
      self::$MsisdnArray = null;
      self::$InvoicesArray = null;


      $this->logSection('tel num', 'added :' . $tel . ' Memory usage : ' . memory_get_usage());
      $duration = microtime(true) - $this->startTime;
      $this->logSection('payment : ', sprintf('added in %s', $duration));
    }
  }

关于功能:
   $this->initInvoicesArray($invoice);
   $this->prepareInvoice();

   $this->prepareUserProfile($client);
   $this->prepareClient();

它们只是为了准备$this->client

这是每个循环所显示的内存使用情况:

>> tel num   added :0699946185 Memory usage : 89287596
>> payment :  added in 8.6373870372772
>> tel num   added :0699983919 Memory usage : 165854544
>> payment :  added in 18.373502969742
>> tel num   added :0699949623 Memory usage : 241338788
>> payment :  added in 29.336947917938
>> tel num   added :0699854750 Memory usage : 319173092
>> payment :  added in 40.880628824234

正如您所看到的,我试图释放这些变量的内存:

$results = null;
$collection = null;
self::$MsisdnArray = null;
self::$InvoicesArray = null;

但是徒劳无功,内存使用量在每次循环后都会增加,这导致了一个“允许的内存大小已用尽”的致命错误。我该如何进行优化?

谢谢。


1
你正在使用哪个 PHP 版本?垃圾回收器从 5.2 版本更新到 5.3 版本,这正好解决了你的问题。 - Marc
1
你读完文件后也关闭了它吗? - Sanjay Kumar N S
1
你正在以哪种模式打开文件? - Sanjay Kumar N S
读取模式:$this->file = new SplFileObject($this->filePath, 'r'); $this->file->setFlags(SplFileObject::READ_CSV); - SmootQ
1
好的,我仍然建议您阅读http://php.net/manual/en/features.gc.performance-considerations.php,如果这没有帮助,至少了解一下也是很好的。 - Marc
显示剩余3条评论
3个回答

3

Doctrine对象存在循环引用,php难以释放。为解决此问题,Doctrine提供了free()方法,在将集合设置为null之前调用该方法:

$collection->free(true);
$collection = null;

1
亲爱的朋友,太棒了+1。我添加了$collection->free(),这是当前的结果内存使用情况:59681588、106647676、152936232……等等。现在它在10个循环后停止(之前在7个循环后崩溃)。不错,非常感谢。但是内存使用仍在增加。 - SmootQ
1
Doctrine_Query 还有 free() 方法,可以尝试使用。 - Marek
我会加进去,然后看看。 - SmootQ
它仍然运行10个循环,然后发送内存致命错误,但是在每个循环中内存使用量再次减少,再次感谢您。 - SmootQ
1
很抱歉,您唯一的选择是为文件中的每X行执行单独的进程。 - Marek
显示剩余2条评论

1
调用$this->prepareAndSaveValidClients();之后,您还可以使用gc_collect_cycles()函数。它可以帮助解决可能的内存泄漏问题。

谢谢,我通过直接在选择查询中使用SQL和PDO(而不是Doctrine)解决了问题。 - SmootQ

0

我在2个月前解决了这个问题,内存使用率变得稳定了,但是我忘记在这里发布解决方案了。

我的做法是移除Doctrine,改用纯PDO和Sql进行查询


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接