1

我有一个 cron 作业,它向订阅者列表发送电子邮件,在 foreach 循环中一次发送一个,并带有 PDF 附件。我从 cron 脚本中收到了这条消息:

Fatal error: Allowed memory size of 94371840 bytes exhausted (tried to allocate 78643193 bytes)

我需要做些什么来防止这个错误?

另外,我很确定它没有完成发送给所有订阅者,那么我应该如何跟踪它,以便它知道如果它没有发送给每个人,它知道在哪里再次接收?

更新程序:这是一个代码示例:(顺便说一下,我正在使用 Zend 框架)

public function send(Default_Model_MyEmail $myEmail)
{
    if (null != ($id = $myEmail->attachmentId)) {
        $file = new Default_Model_File();
        $file->find($id);
        $filepath = APPLICATION_UPLOADS_DIR . '/' . $file->getActualFilename();

        $attachment = new Zend_Mime_Part(file_get_contents($filepath));
        $attachment->type = $file->getMimeType();
        $attachment->disposition = Zend_Mime::DISPOSITION_ATTACHMENT;
        $attachment->encoding = Zend_Mime::ENCODING_BASE64;
        $attachment->filename = $file->getDisplayFilename();    
    }
    $transport = new Zend_Mail_Transport_Smtp('localhost');

    $mail = new Zend_Mail('utf-8');
    $mail->setFrom('from@address', 'From Name');
    $mail->setReplyTo('replyto@address');
    $mail->setSubject($myEmail->subject);
    if (isset($attachment)) {
        $mail->addAttachment($attachment);
    }

    $subscribers = $this->getSubscribers();
    foreach ($subscribers as $subscriber) {
        $mail->addTo($subscriber->email);
        $bodyText = $myEmail->body
            . "\r\n\r\nIf for any reason you would like to be removed from this mailing list, "
            . "please visit \r\nhttp://myapp.com/myemail/unsubscribe/email/"
            . $subscriber->email;
        $mail->setBodyText($bodyText);
        $mail->send($transport);
        $mail->clearRecipients();
    }
}

更新:我正在重用$transport变量。我的印象是这是发送给多个订阅者的正确方法,但也许这就是原因?你怎么看?

更新:我添加了一堆打印内存使用语句的日志语句,但我现在真的不知道该怎么做。内存使用量随着每封电子邮件而增加。订阅者列表为 200,它达到 160,然后内存不足。我应该怎么办?

4

4 回答 4

2

看起来您的代码正试图根据错误消息分配 78MB 的块。

检查您的代码是否有任何可能尝试一次分配大量内存的内容。这可能不是由于未能释放较小的对象引起的,因为失败的分配块很大。

如果您发布导致此问题的代码片段,我很乐意查看并尝试更详细的回复。

至于确定您是否发送给所有人,检查您的电子邮件服务器(SMTP 服务器)会写入已发送消息的日志。如果是这样,您可能能够获得收到电子邮件的人员列表。一般来说,我建议您修改您的 PHP 代码以将每封发送的电子邮件记录到文件或数据库中,以防您将来发生崩溃。

看到代码后编辑:

从表面上看,代表电子邮件和附件的对象似乎是创建一次并重复使用的。

我建议你在本地调试代码。

首先,建立一个与生产环境相同的内存限制。试试这个资源来了解如何。

然后,在循环中添加一些调试输出,以查看每次迭代后有多少可用内存。

最后,在本地运行代码,但最好在您的域中替换您的电子邮件地址或已知的错误电子邮件地址(这样您就不会向人们发送垃圾邮件)。在发送过程中观察内存使用情况。

这有望帮助您缩小错误原因的范围。

编辑2:

好的,在看到内存使用量持续增长之后,谷歌发现了一个小魔术,这是一个已知问题,有报告的解决方法

于 2009-12-31T19:40:10.830 回答
1

除了@Malfist 的回答,您还可以:

  • 确保您重用变量,而不是为循环的每次迭代添加新变量。
  • 让系统在邮件发送成功时进行记录,并在发送一定数量的邮件后停止发送。下次 cron 运行时,它应该确定要发送给谁并恢复。
于 2009-12-31T19:41:37.770 回答
1
ini_set("memory_limit","128M");
于 2013-08-05T04:29:19.880 回答
0

减少内存使用,或增加内存限制。

于 2009-12-31T19:39:17.523 回答