Diagnostic des Fuites de Mémoire - Allowed memory size of # bytes exhausted
J'ai rencontré le redoutable message d'erreur, éventuellement à travers les efforts laborieux, PHP a exécuter de mémoire:
Allowed memory size of #### bytes exhausted (tried to allocate #### octets) file.php sur la ligne 123
L'augmentation de la limite
Si vous savez ce que vous faites et que vous voulez augmenter la limite voir memory_limit:
ini_set('memory_limit', '16M');
ini_set('memory_limit', -1); //no limit
Méfiez-vous! Vous ne pouvez résoudre le symptôme et pas la de problème!
Diagnostiquer la fuite:
Le message d'erreur de points d'une ligne à l'intérieur d'une boucle que je crois être une fuite, ou inutilement-accumulation, de la mémoire. Je l'ai imprimé memory_get_usage()
consolidés à la fin de chaque itération et voir le nombre de grandir lentement jusqu'à ce qu'il atteigne la limite:
foreach ($users as $user) {
$task = new Task;
$task->run($user);
unset($task); //Free the variable in an attempt to recover memory
print memory_get_usage(true); //increases over time
}
Pour les fins de cette question, supposons le pire de code spaghetti imaginables se cache dans le champ d'application mondial, quelque part dans $user
ou Task
.
Quels outils, PHP astuces, ou de débogage vaudou peut m'aider à trouver et résoudre le problème?
- P. S.-j'ai récemment un problème avec ce type exact de la chose. Malheureusement, j'ai aussi trouvé que php a un enfant de destruction des objets problème. Si vous enlevez un objet parent, son enfant objets ne sont pas libérés. Avoir à vous assurer que j'ai utiliser une version modifiée de la fonction unset (qui comprend un appel récursif de tous les objets enfants __autodétruire et ainsi de suite. Plus de détails ici: paul-m-jones.com/archives/262 :: je suis en train de faire quelque chose comme: fonction super_unset( $item ){ if( is_object( $item ) && method_exists( $item, "__autodétruire" ) ){ $article->__autodétruire(); } unset( $item ); }
Vous devez vous connecter pour publier un commentaire.
PHP ne dispose pas d'un garbage collector. Il utilise le comptage de référence pour gérer la mémoire. Ainsi, la source la plus commune de fuites de mémoire sont cyclique des références et des variables globales. Si vous utilisez un framework, vous aurez beaucoup de code à parcourir pour le trouver, je le crains. Le plus simple instrument est à placer sélectivement les appels à
memory_get_usage
et le réduire à l'endroit où le code des fuites. Vous pouvez également utiliser xdebug pour créer une trace du code. Exécuter le code avec les traces d'exécution etshow_mem_delta
.unset()
) et de test. Une fois j'ai trouvé quelque chose qui a eu un effet sur les fuites de mémoire, je savais que j'étais à faire des progrès! Espérons que cette aide! 🙂Il y a plusieurs points de fuite de mémoire en php:
Il est assez difficile de trouver et de corriger les 3 premiers sans profondeur de l'ingénierie inverse ou le code source php de connaissances. Pour le dernier, vous pouvez utiliser la recherche binaire pour la mémoire de la fuite de code avec la fonction memory_get_usage
Ici est une astuce que nous avons utilisé pour identifier les scripts qui utilisent le plus de mémoire sur notre serveur.
Enregistrer le fragment de code suivant dans un fichier, par exemple,
/usr/local/lib/php/strangecode_log_memory_usage.inc.php
:L'employer par adjonction de httpd.conf:
Alors analyser le fichier journal à
/var/log/httpd/php_memory_log
Vous pourriez avoir besoin pour
touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log
avant votre web utilisateur peut écrire dans le fichier journal.J'ai remarqué qu'une fois dans un vieux script PHP permettrait de maintenir le "comme" variable comme dans le champ d'application, même après ma boucle foreach. Par exemple,
Je ne suis pas sûr si les futures versions de PHP fixe ou non depuis que je l'ai vu. Si c'est le cas, vous pourriez
unset($user)
après ladoSomething()
ligne pour l'effacer de la mémoire. YMMV.unset()
, mais garder à l'esprit que pour les objets, tout ce que vous faites est de la modification de l'emplacement de votre variable pointe vers - vous n'avez pas réellement supprimé de la mémoire. PHP va automatiquement libérer de la mémoire une fois qu'il est hors de portée de toute façon, la meilleure solution (en termes de cette réponse, pas le cas des OP question) est d'utiliser les fonctions de sorte qu'ils ne sont pas accrochés à la variable de la boucle pour trop longtemps.J'ai récemment rencontré ce problème sur une demande, en vertu de ce que je comprends à des circonstances similaires. Un script qui s'exécute en PHP cli qui passe en boucle sur de nombreuses itérations. Mon script dépend de plusieurs bibliothèques sous-jacentes. Je pense que l'un particulier de la bibliothèque est la cause et j'ai passé plusieurs heures en vain d'essayer d'ajouter approprié destruction des méthodes de classes en vain. Face à un long processus de conversion à une autre bibliothèque (qui pourrait avoir les mêmes problèmes), je suis venu avec un brut de contourner le problème dans mon cas.
Dans ma situation, sur une ligne de commande linux, j'étais en boucle sur un tas d'enregistrements d'utilisateurs et pour chacun d'eux, créant une nouvelle instance de plusieurs classes que j'ai créé. J'ai décidé d'essayer de la création de nouvelles instances des classes à l'aide de PHP exec méthode, de sorte que ces processus de s'exécuter dans un "nouveau fil". Ici est un exemple basique de quoi je parle:
Évidemment, cette approche a ses limites, et l'on doit être conscient des dangers de ce, comme il serait facile de créer un lapin travail, cependant dans certains cas rares, il peut aider à obtenir plus d'un endroit difficile, jusqu'à ce qu'une meilleure correction pourrait être trouvé, comme dans mon cas.
Je suis tombé sur le même problème, et ma solution a été de remplacer foreach avec un régulier pour. Je ne suis pas sûr de connaître les détails, mais il semble comme foreach crée une copie (ou en quelque sorte une nouvelle référence à l'objet. À l'aide d'un régulier pour la boucle, vous accédez directement l'article.
J'ai récemment remarqué que PHP 5.3 lambda fonctions supplémentaires de mémoire utilisés lorsqu'ils sont retirés.
Je ne sais pas pourquoi, mais il semble prendre un supplément de 250 octets chacun lambda, même après la fonction est supprimée.
Je vous suggère de vérifier le manuel php ou ajouter le
gc_enable()
fonction de collecter les ordures... Qui est la mémoire des fuites de ne pas affecter la façon dont votre code s'exécute.PS: php dispose d'un garbage collector
gc_enable()
qui ne prend pas d'arguments.Si ce que vous dites à propos de PHP ne fait que la GC après qu'une fonction est vrai, vous pouvez envelopper le contenu de la boucle à l'intérieur d'une fonction comme une solution de contournement/expérience.
run()
qui est appelé est aussi une fonction, à la fin de laquelle le GC doit arriver.Un énorme problème que j'ai eu était en utilisant create_function. Comme dans les lambda fonctions, il quitte l'généré nom temporaire dans la mémoire.
Une autre cause de fuites de mémoire (dans le cas de Zend Framework) est le Zend_Db_Profiler.
Assurez-vous que est désactivé si vous exécutez des scripts sous Zend Framework.
Par exemple j'ai eu dans mon application.ini ce qui suit:
D'une durée d'environ 25.000 requêtes + charges de traitement avant que, a introduit la mémoire d'une belle 128 mo (Mon max de la limite de mémoire).
Par juste milieu:
c'était suffisant pour la maintenir en dessous de 20 Mo
Et ce script a été exécuté dans la CLI, mais c'était l'instanciation de la Zend_Application et de l'exécution de l'Amorce, de sorte qu'il a utilisé le "développement" config.
Il a vraiment aidé à lancer le script avec xDebug de profilage
Je ne l'ai pas vu mentionné explicitement, mais xdebug fait un excellent travail de profilage temps et de la mémoire (comme de 2.6). Vous pouvez prendre les informations qu'il génère et de passer à un frontal graphique de votre choix: webgrind (temps), kcachegrind, qcachegrind ou autres, et il génère très utiles d'appel d'arbres et de graphes pour vous permettre de trouver les sources de vos divers malheurs.
Exemple (de qcachegrind):
Je suis un peu en retard à cette conversation, mais je vais partager quelque chose de pertinent à Zend Framework.
J'ai eu un problème de fuite de mémoire après l'installation de php 5.3.8 (à l'aide de phpfarm) à travailler avec une ZF application qui a été développé avec php 5.2.9. J'ai découvert que la fuite de mémoire a été déclenchée dans Apache httpd.fichier conf, dans ma définition des serveurs virtuels, où il est dit
SetEnv APPLICATION_ENV "development"
. Après avoir commenté cette ligne, les fuites de mémoire arrêté. J'essaie de venir avec une ligne de solution de contournement dans mon script php (principalement en définissant manuellement dans les principaux index.php fichier)."development"
environnement a généralement un tas de logging & l'établissement de profils d'autres environnements ne pourrait pas avoir. Commentaires de la ligne juste fait de votre utilisation de l'application par défaut de l'environnement au lieu de cela, ce qui est généralement"production"
ou"prod"
. La fuite de mémoire existe encore; le code qu'il contient n'est tout simplement pas être appelée dans cet environnement.Je ne le vois pas mentionné ici, mais une chose qui pourrait être utile est d'utiliser xdebug et xdebug_debug_zval('variableName') pour voir le refcount.
Je peux également fournir un exemple d'une extension php arriver dans le chemin: Zend Server Z-Ray. Si la collecte de données est activé, l'utilisation de la mémoire sera ballon à chaque itération, comme si la collecte des déchets était à l'arrêt.