60 votes

Forcer la libération de mémoire en PHP

dans un programme PHP, je séquentiellement lire un tas de fichiers (avec file_get_contents), gzdecode de leur, json_decode le résultat, d'analyser le contenu, de le jeter le plus de loin, et de stocker environ 1% dans un tableau. Malheureusement, à chaque itération (je traverse sur un tableau contenant les noms de fichiers), il semble y avoir de la mémoire perdue (selon memory_get_peak_usage, environ 2 à 10 MO à chaque fois). J'ai en double et triplechecked mon code, je ne suis pas stocker unneded données dans la boucle (et les données nécessaires à peine plus d'environ 10 MO au total), mais je suis souvent la réécriture (en fait, des chaînes de caractères dans un tableau). Apparemment, PHP ne libère pas la mémoire correctement, donc l'utilisation de plus en plus de mémoire vive jusqu'à ce qu'il arrive à la limite. Est-il possible de forcer une collecte des ordures? Ou, au moins, pour savoir où la mémoire est utilisée?

Merci d'avance, Dmitri

39voto

James Lyons Points 241

il a à voir avec la fragmentation de la mémoire.

Considérons deux chaînes, concaténées en une seule chaîne. Chaque original doit rester jusqu'à ce que la sortie est créée. La sortie est plus longue que celle d'entrée.
Par conséquent, à une nouvelle répartition doit être faite pour stocker le résultat d'une concaténation. Les chaînes d'origine sont libérés , mais ils sont de petits blocs de mémoire.
Dans un cas d' 'str1' . 'str2' . 'str3' . 'str4' vous avez plusieurs temps d'être créé à chaque . -- et aucun d'entre eux correspond à l'espace des thats été libéré. Les cordes ne sont pas énoncées dans la mémoire contiguë (qui est, chaque chaîne est, mais les différentes chaînes ne sont pas mis fin à la fin) dues à d'autres utilisations de la mémoire. Donc la libération de la chaîne crée un problème parce que l'espace ne peut pas être réutilisée de manière efficace. Afin de vous grandir avec chaque tmp que vous créez. Et vous n'avez pas re-utiliser quoi que ce soit, jamais.

À l'aide de la matrice en fonction implode, vous ne créez qu'1 sortie-exactement la longueur que vous désirez. Réalisant seulement 1 allocation supplémentaire. Donc, elle est beaucoup plus efficace en terme de mémoire et de ne pas subir à partir de la concaténation de la fragmentation. En est de même de python. Si vous avez besoin de concaténer des chaînes, plus de 1 concaténation doit toujours être basé sur les tableau:

''.join(['str1','str2','str3'])

en python

implode('', array('str1', 'str2', 'str3'))

en PHP

sprintf équivalents sont aussi très bien.

La mémoire signalée par memory_get_peak_usage est toujours, fondamentalement, la "dernière" peu de mémoire dans la carte virtuelle il avait à utiliser. Donc, depuis son toujours de plus en plus, il connaît une croissance rapide. Comme chaque allocation tombe "à la fin" du bloc de mémoire.

27voto

Mo. Points 675

En PHP> = 5.3.0, vous pouvez appeler gc_collect_cycles() pour forcer une passe GC.

15voto

DBa Points 283

Trouvé la solution: il était une concaténation de chaîne. J'étais la génération de la ligne de saisie en ligne par la concaténation de certaines variables (la sortie est un fichier CSV). Toutefois, PHP ne semble pas libérer la mémoire utilisée pour l'ancien copie de la chaîne, ainsi efficacement casser la RAM avec les données inutilisées. Le passage à une approche basée sur la baie (et implosion par des virgules juste avant fputs-ing à la outfile) contourné ce problème.

Pour une raison pas évident pour moi - PHP signalé l'augmentation de l'utilisation de la mémoire au cours de la fonction json_decode appels, ce qui me tromper à l'hypothèse que la fonction json_decode était le problème.

11voto

Mike B Points 18950

J'ai constaté que le gestionnaire de mémoire interne de PHP était le plus susceptible d'être appelé à la fin d'une fonction. Sachant cela, j'ai refactored le code dans une boucle comme ceci:

 while (condition) {
  // do
  // cool
  // stuff
}
 

à

 while (condition) {
  do_cool_stuff();
}

function do_cool_stuff() {
  // do
  // cool
  // stuff
}
 

MODIFIER

J'ai exécuté ce test rapide et je n'ai pas constaté d'augmentation de l'utilisation de la mémoire. Cela me porte à croire que la fuite n'est pas dans json_decode()

 for($x=0;$x<10000000;$x++)
{
  do_something_cool();
}

function do_something_cool() {
  $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}';
  $result = json_decode($json);
  echo memory_get_peak_usage() . PHP_EOL;
}
 

6voto

Andy Points 8860

Appelez memory_get_peak_usage() après chaque instruction, et vous assurer d' unset() tout ce que vous pouvez. Si vous êtes itération avec des foreach(), l'utilisation d'une variable en question pour éviter de faire une copie de l'original (foreach()).

foreach( $x as &$y)

Si PHP est en fait une fuite de mémoire oblige, la collecte des ordures ne fera aucune différence.

Il y a un bon article sur PHP fuites de mémoire et de leur détection à IBM

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X