2009-12-31 3 views
1

J'ai un travail cron qui envoie des emails à une liste d'abonnés, un à la fois dans une boucle foreach, avec une pièce jointe PDF. J'ai reçu ce message du script cron:Zend_Mail: Comment réparer l'erreur fatale PHP: la taille de la mémoire autorisée est-elle épuisée?

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

Que dois-je faire pour éviter cette erreur? En outre, je suis presque sûr qu'il n'a pas fini d'envoyer à tous les abonnés, alors comment dois-je garder une trace de ceci afin qu'il sache où reprendre à nouveau s'il ne l'envoyait pas à tout le monde?

Updater: Voici un exemple de code: (J'utilise le Zend Framework en passant)

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('[email protected]', 'From Name'); 
    $mail->setReplyTo('[email protected]'); 
    $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(); 
    } 
} 

Mise à jour: Je réutilisez la variable $transport. J'étais sous l'impression que c'était la bonne façon d'envoyer à plusieurs abonnés, mais peut-être que c'est la cause? Qu'est-ce que tu penses?

Mise à jour: J'ai ajouté un tas d'instructions de journal qui impriment des instructions d'utilisation de la mémoire, mais je ne sais pas vraiment quoi faire maintenant. L'utilisation de la mémoire augmente avec chaque email. Avec une liste d'abonnés de 200, il arrive à 160 puis manque de mémoire. Que devrais-je faire?

Répondre

0

Réduit l'utilisation de la mémoire ou augmente la limite de mémoire.

+2

C'est une réponse terriblement détaillée :-) –

+3

Il est une question terriblement détaillée. – Malfist

+0

Et pourtant toutes les autres réponses offrent un point de départ pour trouver une solution ... –

2

Il semble que votre code essaie d'allouer un bloc de 78 Mo en fonction du message d'erreur.

Vérifiez votre code pour tout ce qui pourrait essayer d'allouer une très grande partie de la mémoire à la fois. Cela n'est probablement pas dû à l'échec de la libération d'objets plus petits, car le bloc d'allocation ayant échoué est volumineux.

Si vous postez un extrait de code qui en est la cause, je serais ravi d'y jeter un coup d'œil et d'essayer une réponse plus détaillée. Pour savoir si vous avez envoyé à tout le monde ou non, la vérification de votre serveur de messagerie (serveur SMTP) écrit un journal des messages envoyés. Si c'est le cas, vous pourrez peut-être obtenir une liste des personnes qui ont reçu l'e-mail. En général, je vous suggère de modifier votre code PHP pour enregistrer dans un fichier ou une base de données chaque email qui a été envoyé, dans le cas où vous avez un accident dans le futur.

EDIT après avoir vu le code:

Sur la surface, il semble que l'objet qui représente l'e-mail et l'attachement est créé une fois et réutilisée.

Je vous suggère de déboguer le code localement. Commencez par établir une limite de mémoire égale à celle que vous avez en production. Essayez this resource pour comprendre comment.

Ensuite, ajoutez une sortie de débogage dans votre boucle pour voir combien memory is available après chaque itération.

Enfin, exécutez le code localement, mais remplacez soit votre adresse e-mail ou une adresse e-mail mauvaise connue de préférence à votre domaine (afin de ne pas les gens anti-spam). Surveillez l'utilisation de la mémoire au fur et à mesure que votre envoi progresse.

Cela devrait vous aider à réduire la cause de l'erreur.

EDIT 2:

OK, après avoir vu que l'utilisation de la mémoire ne cesse d'augmenter, un peu de magie de Google relevai que c'est un problème connu avec un reported workaround.

+0

a ajouté un échantillon de code – Andrew

+0

J'ai fait ce que vous avez dit, j'ai un journal plein d'énoncés d'utilisation de la mémoire, mais je ne sais pas vraiment quoi à faire à ce sujet. L'utilisation de la mémoire augmente avec chaque email. Avec une liste d'abonnés de 200, il arrive à 160 puis manque de mémoire. – Andrew

+0

Trouvé une solution probable. Voir EDIT 2 dans ma réponse. –

1

En plus de @ réponse de Malfist, vous pouvez:

  • Assurez-vous de réutiliser des variables au lieu d'ajouter de nouvelles pour chaque itération de la boucle.
  • Demander au système de prendre note de l'envoi d'un e-mail et d'arrêter l'envoi après un certain nombre d'e-mails. La prochaine fois que cron s'exécutera, il devrait déterminer qui doit être envoyé et reprendre.
1
ini_set("memory_limit","128M"); 
Questions connexes