Erreur fatale: taille de mémoire autorisée de 134217728 octets épuisée (CodeIgniter + XML-RPC)

619

J'ai un tas de systèmes de point de vente (POS) client qui envoient régulièrement de nouvelles données de vente à une base de données centralisée, qui stocke les données dans une grande base de données pour la génération de rapports.

Le POS client est basé sur PHPPOS, et j'ai implémenté un module qui utilise la bibliothèque XML-RPC standard pour envoyer des données de vente au service. Le système serveur est construit sur CodeIgniter et utilise les bibliothèques XML-RPC et XML-RPCS pour le composant de service Web. Chaque fois que j'envoie beaucoup de données sur les ventes (aussi peu que 50 lignes de la table des ventes et des lignes individuelles de sales_items relatives à chaque article de la vente), j'obtiens l'erreur suivante:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 54 bytes)

128M est la valeur par défaut php.ini, mais je suppose que c'est un nombre énorme à casser. En fait, j'ai même essayé de définir cette valeur à 1024M, et tout ce qu'il faut, c'est prendre plus de temps pour l'erreur.

En ce qui concerne les mesures que j'ai prises, j'ai essayé de désactiver tous les traitements côté serveur et je les ai truqués pour renvoyer une réponse standardisée quelle que soit l'entrée. Cependant, je pense que le problème réside dans l'envoi réel des données. J'ai même essayé de désactiver le temps d'exécution maximal du script pour PHP, et cela continue de produire des erreurs.

ArcticZero
la source
5
Je suis un peu confus ... où l'erreur se produit-elle sur le client ou le serveur? Et à quelle étape ... envoi client, réception serveur, traitement serveur, envoi serveur, réception client ou traitement client?
Greg
2
L'erreur semble se produire soit lors de l'envoi du client, soit lors de la réception du serveur. J'ai essayé de désactiver tous les traitements côté serveur et de les configurer pour envoyer une réponse standardisée quelles que soient les données envoyées. L'erreur se produit si j'envoie une certaine quantité de données. Je modifie le paramètre PHP.ini.
ArcticZero
42
la limite de mémoire est de 128 Mo, souble it:ini_set('memory_limit', '256M');
9
Résumé a voté contre toutes les réponses "ignorer la fuite", les gens qui ont confondu CodeIgniter avec Drupal et les gens qui ont juste copié et collé les réponses des autres pour obtenir des points. La qualité des réponses dans celle-ci est épouvantable.
Matti Virkkunen

Réponses:

697

Changer le memory_limitby ini_set('memory_limit', '-1');n'est pas une bonne solution. S'il vous plaît ne faites pas ça.

Votre code PHP peut avoir une fuite de mémoire quelque part et vous dites au serveur d'utiliser simplement toute la mémoire qu'il souhaite. Vous n'auriez pas résolu le problème du tout. Si vous surveillez votre serveur, vous verrez qu'il utilise maintenant probablement la plus grande partie de la RAM et qu'il est même échangé sur le disque.

Vous devriez probablement essayer de retrouver le code incriminé dans votre code et de le corriger.

Jeff
la source
174
@Jeff vous avez probablement raison 95% du temps. Cependant, il y a des moments où vous avez réellement besoin de plus de mémoire. Par exemple, supposons que votre application charge une énorme quantité de données en mémoire pour le traitement (disons une nomenclature avec 15 000 composants). Ce n'est pas toujours le cas si le code est bogué, parfois vous avez juste besoin d'un peu plus de mémoire (par exemple 256M au lieu de 128M). Cependant, je suis d'accord pour dire que le régler sur -1 est horriblement mauvais. Mais ajuster la limite de mémoire pour des situations raisonnables au moment de l'exécution est parfaitement acceptable à mon humble avis.
Pyrite
24
@pyrite oui vous avez raison: parfois un processus nécessite plus de mémoire mais vous devez augmenter la limite de mémoire à une quantité logique comme 256 Mo comme vous l'avez dit ou 512 Mo pourquoi pas MAIS pas -1;)
Lukas Lukac
9
@jeff Je suis entièrement d'accord, une valeur de -1ne pourrait être utile que dans des environnements de développement à des fins de test.
Esolitos
4
@Pyrite dans les cas que vous avez nommés pour les 5% restants lisent les données en morceaux et utilisent un travailleur pour les traiter au lieu d'utiliser plus de mémoire. Cette solution évoluera également tandis que votre suggestion ne fonctionnera pas, sauf si vous continuez à bourrer de plus en plus de mémoire dans votre serveur au fil du temps si les données augmentent.
burzum
2
De la manière la plus courante, ce problème dans ORM lorsque vous essayez de récupérer toutes les données limite la mémoire php. Par exemple, lorsque vous essayez de générer un rapport mensuel.
Stepchik
213

ini_set('memory_limit', '-1');remplace la limite de mémoire PHP par défaut .

Chris Lane
la source
16
@williamcarswell; -1est une valeur que PHP considère comme illimitée dans ce contexte.
Alix Axel
7
@ ArseniuszŁozicki - il consommera également des ressources que le serveur ne peut pas épargner.
Ken Williams
124
Dommage que cela reçoive autant de votes positifs. La définition d'une valeur précise, avec des modifications php.ini ou ini_set, est une solution parfaitement valide lorsque les gens ont besoin de plus de mémoire. Le définir sur illimité est un hack dangereux :(
Jeff Davis
24
@ user1767586 puis définissez-le sur une valeur saine. Vous pouvez empêcher le script de générer l'erreur en le définissant sur 1024 Mo. Si cette réponse disait ini_set ('memory_limit', '1024M'); Vous pouvez copier-coller cela et être d'accord. En le définissant sur -1, vous vous configurez pour avoir un script qui consomme toute la mémoire. Surtout si vous le faites régulièrement. Mettre "dangereux" entre guillemets ne le rend pas moins dangereux. Vous pourriez vraiment arroser votre serveur hôte. Peut-être commencer à détruire des données. Je ne sais pas, peut-être perdre ton travail? Cela me semble assez dangereux. : |
Jeff Davis
3
Triste de voir que la réponse pour +161 votes et -3 votes est la même :(
akarthik10
130

La bonne façon est de modifier votre php.inifichier. Modifiez memory_limità votre valeur désirée.

À partir de votre question, 128M(qui est la limite par défaut) a été dépassée, il y a donc quelque chose de grave avec votre code car il ne devrait pas en prendre autant.

Si vous savez pourquoi cela prend autant et que vous souhaitez lui permettre de définir memory_limit = 512Mou supérieur et vous devriez être bon.

Basav
la source
7
Honnêtement, si vous mettez en cache de grandes quantités de données, c'est la bonne réponse. 128M n'est pas suffisant pour certains scripts. 512M ou 1024M seront souvent suffisants, mais vous devez décider au cas par cas.
Jeff Davis
2
Yeha, cependant, essayez d'éviter une énorme utilisation de la mémoire, si le nombre d'utilisateurs va être plus important
Basav
2
memory_limit = -1; défini dans php.ini
2
@YumYumYum Cela supprime le memory_limit, que vous ne voulez que si vous surveillez l'utilisation de la mémoire d'une autre manière. Le système d'exploitation va tuer le processus s'il prend une énorme quantité de mémoire à un moment donné.
Flimm
Donc, si vous exécutez un script qui utilise beaucoup de mémoire, mais que vous ne devez l'exécuter qu'une seule fois, pouvez-vous simplement augmenter la limite de mémoire pour le processus au moment de l'exécution, puis réduire à nouveau votre limite de mémoire après votre utilisation unique le script s'exécute?
chromechris
95

L'allocation de mémoire pour PHP peut être ajustée de manière permanente ou temporaire.

En permanence

Vous pouvez modifier définitivement l'allocation de mémoire PHP de deux manières.

Si vous avez accès à votre php.inifichier, vous pouvez modifier la valeur memory_limitde votre valeur désirée.

Si vous n'avez pas accès à votre php.inifichier (et que votre hébergeur le permet), vous pouvez remplacer l'allocation de mémoire via votre .htaccessfichier. Ajoutez php_value memory_limit 128M(ou quelle que soit votre allocation souhaitée).

Temporaire

Vous pouvez ajuster l'allocation de mémoire à la volée à partir d'un fichier PHP. Vous avez simplement le code ini_set('memory_limit', '128M');(ou quelle que soit votre allocation souhaitée). Vous pouvez supprimer la limite de mémoire (bien que des limites de machine ou d'instance puissent toujours s'appliquer) en définissant la valeur sur "-1".

Umair Idrees
la source
2
Merci, je n'ai pas pensé à vérifier si quelqu'un avait défini la valeur dans .htaccess qui remplaçait php.ini et je ne pouvais pas comprendre pourquoi +1
HostMyBus
61

Il est très facile d'obtenir des fuites de mémoire dans un script PHP - surtout si vous utilisez l'abstraction, comme un ORM. Essayez d'utiliser Xdebug pour profiler votre script et découvrir où est passée toute cette mémoire.

troelskn
la source
1
Je vais essayer Xdebug. Je ne l'ai jamais utilisé auparavant, je vais donc devoir le lire. Merci d'avoir répondu! J'espère que je trouverai bientôt la réponse à cette question ...
ArcticZero
34
N'oubliez pas que PHP utilise le comptage des références pour gérer la mémoire. Donc, si vous avez des références circulaires ou des variables globales, ces objets ne seront pas recyclés. C'est généralement la racine des fuites de mémoire en PHP.
troelskn
Xdebug montre que la bibliothèque Xmlrpc.php de CI est responsable de ma fuite de mémoire. Par hasard, y aurait-il des problèmes avec les bibliothèques XML-RPC de CodeIgniter que je devrais connaître? J'ai essayé de désactiver tous les traitements côté serveur, et il manque toujours de mémoire si je lui donne suffisamment de données.
ArcticZero
1
Je ne connais pas / n'utilise pas CI, donc je ne sais pas. Mais vous devriez probablement essayer de trouver un objet qui n'est pas libéré après utilisation - très probablement à cause d'une référence cyclique. C'est du travail de détective.
troelskn
1
C'est la seule réponse ici qui conseille réellement de résoudre le problème. Les autres réponses augmentent la mémoire pour panser un symptôme et ignorer la maladie .
Chris Baker du
56

Lors de l'ajout de 22,5 millions d'enregistrements dans un tableau avec array_push, je continuais à obtenir des erreurs fatales "mémoire épuisée" à environ 20 millions d'enregistrements en utilisant 4Gcomme limite de mémoire dans le fichier php.ini. Pour résoudre ce problème, j'ai ajouté la déclaration

$old = ini_set('memory_limit', '8192M');

en haut du fichier. Maintenant, tout fonctionne bien. Je ne sais pas si PHP a une fuite de mémoire. Ce n'est pas mon travail et je m'en fiche. Je dois juste faire mon travail, et cela a fonctionné.

Le programme est très simple:

$fh = fopen($myfile);
while (!feof($fh)) {
    array_push($file, stripslashes(fgets($fh)));
}
fclose($fh);

L'erreur fatale pointe vers la ligne 3 jusqu'à ce que j'augmente la limite de mémoire, ce qui élimine l'erreur.

JamesAD-0
la source
10
tu veux dire ini_set('memory_limit', '8192M');?
Gogol
2
Quel luxe ce serait d'avoir le temps d'aller optimiser un script pour quelque chose comme ça. Ou recherchez et comparez et apprenez les outils ETL ou certains autres. Dans le monde réel, nous augmentons la capacité de mémoire, faisons la chose et continuons.
Matthew Poer
45

J'ai continué à obtenir cette erreur, même avec memory_limitset in php.ini, et la valeur se lisait correctement avec phpinfo().

En le changeant de ceci:

memory_limit=4G

Pour ça:

memory_limit=4096M

Cela a corrigé le problème dans PHP 7.

Danny Beckett
la source
23

Lorsque vous voyez l'erreur ci-dessus - surtout si la (tried to allocate __ bytes)valeur est faible, cela pourrait être un indicateur d'une boucle infinie, comme une fonction qui s'appelle sans issue:

function exhaustYourBytes()
{
    return exhaustYourBytes();
}
Kristen Waite
la source
19

Après avoir activé ces deux lignes, il a commencé à fonctionner:

; Determines the size of the realpath cache to be used by PHP. This value should
; be increased on systems where PHP opens many files to reflect the quantity of
; the file operations performed.
; http://php.net/realpath-cache-size
realpath_cache_size = 16k

; Duration of time, in seconds for which to cache realpath information for a given
; file or directory. For systems with rarely changing files, consider increasing this
; value.
; http://php.net/realpath-cache-ttl
realpath_cache_ttl = 120

Prem Kumar Maurya
la source
19

Vous pouvez corriger cela correctement en changeant memory_limitsur fastcgi / fpm:

$vim /etc/php5/fpm/php.ini

Changez la mémoire, comme de 128 à 512, voir ci-dessous

; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 128M

à

; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 512M
Derick Fynn
la source
18

Répertoire racine de votre site:

ini_set('memory_limit', '1024M');
Gaurang P
la source
1
cela a fonctionné pour moi. j'adore les solutions en ligne. +1 pour plus de simplicité
Steve C
14

Modifiez la limite de mémoire dans le fichier php.ini et redémarrez Apache. Après le redémarrage, exécutez phpinfo (); fonctionner à partir de n'importe quel fichier PHP pour une memory_limitconfirmation de modification.

memory_limit = -1

La limite de mémoire -1 signifie qu'aucune limite de mémoire n'est définie. C'est maintenant au maximum.

Hasib Kamal
la source
13

Pour les utilisateurs de Drupal, cette réponse de Chris Lane:

ini_set('memory_limit', '-1');

fonctionne mais nous devons le mettre juste après l'ouverture

<?php

dans le fichier index.php du répertoire racine de votre site.

sigmapi13
la source
13

Dans Drupal 7, vous pouvez modifier la limite de mémoire dans le fichier settings.php situé dans votre dossier sites / default. Autour de la ligne 260, vous verrez ceci:

ini_set('memory_limit', '128M');

Même si vos paramètres php.ini sont suffisamment élevés, vous ne pourrez pas consommer plus de 128 Mo si cela n'est pas défini dans votre fichier Drupal settings.php.

LK7889
la source
1
Pas dans Drupal7, il n'y a pas une telle chaîne de code dans settings.php
FLY
Il n'y a pas non plus de chaîne dans settings.php pour drupal 6
AllisonC
12

Plutôt que de modifier la memory_limitvaleur de votre php.inifichier, s'il y a une partie de votre code qui pourrait utiliser beaucoup de mémoire, vous pouvez supprimer memory_limitavant l'exécution de cette section, puis la remplacer après.

$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
mykeels
la source
7

PHP 5.3+ vous permet de modifier la limite de mémoire en plaçant un .user.inifichier dans le public_htmldossier. Créez simplement le fichier ci-dessus et saisissez-y la ligne suivante:

memory_limit = 64M

Certains hôtes cPanel n'acceptent que cette méthode.

Sabi
la source
7

Crash page?

Entrez la description de l'image ici

(Cela se produit lorsque MySQL doit interroger de grandes lignes. Par défaut, il memory_limitest défini sur petit, ce qui était plus sûr pour le matériel.)

Vous pouvez vérifier l'état de la mémoire existante de votre système, avant d'augmenter php.ini:

# free -m
             total       used       free     shared    buffers     cached
Mem:         64457      63791        666          0       1118      18273
-/+ buffers/cache:      44398      20058
Swap:         1021          0       1021

Ici, je l'ai augmenté comme ci-dessous, puis je service httpd restartrésous le problème de la page de blocage.

# grep memory_limit /etc/php.ini
memory_limit = 512M
Peter Mortensen
la source
Quel nombre (ligne et colonne?) On devrait regarder après avoir exécuté la free -mcommande pour décider d'un nouveau memory_limit?
Kiradotee
7

Ajoutez simplement une ini_set('memory_limit', '-1');ligne en haut de votre page Web.

Et vous pouvez définir votre mémoire selon vos besoins à la place de -1, à 16M, etc.

Pankaj Pratik Rai
la source
6
Cela semble dire la même chose que de nombreuses réponses existantes. Il est préférable d'ajouter une réponse à une question populaire uniquement si le nouveau matériel offre quelque chose de nouveau.
halfer
6

Pour ceux qui se grattent la tête pour savoir pourquoi diable cette petite fonction devrait provoquer une fuite de mémoire, parfois par une petite erreur, une fonction commence à s'appeler récursivement pour toujours.

Par exemple, une classe proxy qui porte le même nom pour une fonction de l'objet qui va la proxy.

class Proxy {

    private $actualObject;

    public function doSomething() {

        return $this->actualObjec->doSomething();
    }
}

Parfois, vous pouvez oublier d'apporter ce petit membre actualObjec et parce que le proxy a réellement cette doSomethingméthode, PHP ne vous donnera aucune erreur et pour une grande classe, il pourrait être caché aux yeux pendant quelques minutes pour savoir pourquoi il fuit la mémoire.

madz
la source
Et une autre astuce: vous pouvez mettre die('here')votre code et déplacer cette instruction pour voir où commence la récursivité.
toddmo
6

J'ai eu l'erreur ci-dessous lors de l'exécution sur un ensemble de données plus petit que ce qui avait fonctionné précédemment.

Erreur fatale: taille de mémoire autorisée de 134217728 octets épuisée (tentative d'allocation de 4096 octets) dans C: \ workspace \ image_management.php sur la ligne 173

Comme la recherche de la faute m'a amené ici, j'ai pensé mentionner que ce n'est pas toujours les solutions techniques dans les réponses précédentes, mais quelque chose de plus simple. Dans mon cas, c'était Firefox. Avant d'exécuter le programme, il utilisait déjà 1 157 Mo.

Il s'est avéré que j'avais regardé une vidéo de 50 minutes un peu à la fois sur une période de plusieurs jours et cela a tout gâché. C'est le genre de correctif que les experts corrigent sans même y penser, mais pour moi, cela vaut la peine de garder à l'esprit.

M61Vulcan
la source
J'ai eu un événement similaire sur Google Chrome aujourd'hui. J'étais extrêmement sceptique quant à cette réponse ... cependant, elle a révélé que mon épuisement des octets avait disparu après avoir ouvert une fenêtre de navigation privée et tiré à nouveau le même script! Les recherches se poursuivent.
mickmackusa
2

Exécuter le script comme ceci (cas cron par exemple): php5 /pathToScript/info.phpproduit la même erreur.

La bonne façon: php5 -cli /pathToScript/info.php

ratm
la source
2

Si vous exécutez un VPS (serveur privé virtuel) propulsé par WHM, vous constaterez peut-être que vous n'avez pas l'autorisation de modifier directement PHP.INI; le système doit le faire. Dans le panneau de configuration de l'hôte WHM, accédez à Configuration du serviceÉditeur de configuration PHP et modifiez memory_limit:

Mise à jour de memory_limit sur WHM 11.48.4

Janckos
la source
2

Je trouve cela utile lors de l'inclusion ou de la demande _dbconnection.php_et _functions.phpdans des fichiers réellement traités, plutôt que de l'inclure dans l'en-tête. Ce qui est inclus en soi.

Donc, si votre en- tête et pied de page est inclus, incluez simplement tous vos fichiers fonctionnels avant que l'en-tête ne soit inclus.

Kerim
la source
2

L'utilisation yieldpourrait également être une solution. Voir Syntaxe du générateur .

Au lieu de modifier le PHP.inifichier pour un stockage de mémoire plus important, l'implémentation d' yieldune boucle interne peut parfois résoudre le problème. Ce que fait le rendement, au lieu de vider toutes les données à la fois, il les lit une par une, ce qui économise beaucoup d’utilisation de la mémoire.

LukeDS
la source
2
PHP.ini? N'est-ce pas php.ini?
Peter Mortensen
1

Cette erreur est parfois causée par un bogue dans le code PHP qui provoque des récursions impliquant la gestion des exceptions et éventuellement d'autres opérations. Malheureusement, je n'ai pas pu créer un petit exemple.

Dans ces cas, ce qui m'est arrivé plusieurs fois, set_time_limitéchoue, et le navigateur continue d'essayer de charger la sortie PHP, soit avec une boucle infinie, soit avec le message d'erreur fatal qui est le sujet de cette question.

En réduisant la taille d'allocation autorisée en ajoutant

ini_set('memory_limit','1M');

vers le début de votre code, vous devriez pouvoir empêcher l'erreur fatale.

Ensuite, vous pouvez vous retrouver avec un programme qui se termine, mais est toujours difficile à déboguer.

À ce stade, insérez des BreakLoop()appels dans votre programme pour prendre le contrôle et découvrir quelle boucle ou récursivité dans votre programme est à l'origine du problème.

La définition de BreakLoop est la suivante:

function BreakLoop($MaxRepetitions=500,$LoopSite="unspecified")
    {
    static $Sites=[];
    if (!@$Sites[$LoopSite] || !$MaxRepetitions)
        $Sites[$LoopSite]=['n'=>0, 'if'=>0];
    if (!$MaxRepetitions)
        return;
    if (++$Sites[$LoopSite]['n'] >= $MaxRepetitions)
        {
        $S=debug_backtrace(); // array_reverse
        $info=$S[0];
        $File=$info['file'];
        $Line=$info['line'];
        exit("*** Loop for site $LoopSite was interrupted after $MaxRepetitions repetitions. In file $File at line $Line.");
        }
    } // BreakLoop

L'argument $ LoopSite peut être le nom d'une fonction dans votre code. Ce n'est pas vraiment nécessaire, car le message d'erreur que vous obtiendrez vous dirigera vers la ligne contenant l'appel BreakLoop ().

David Spector
la source
-1

Dans mon cas, c'était un bref problème avec la façon dont une fonction était écrite. Une fuite de mémoire peut être provoquée en affectant une nouvelle valeur à la variable d'entrée d'une fonction, par exemple:

/**
* Memory leak function that illustrates unintentional bad code
* @param $variable - input function that will be assigned a new value
* @return null
**/
function doSomehting($variable){
    $variable = 'set value';
    // Or
    $variable .= 'set value';
}
Dmitriy Kravchuk
la source
-6

Lorsque j'ai supprimé les lignes suivantes de mon code, tout a bien fonctionné!

set_include_path(get_include_path() . get_include_path() . '/phpseclib');
include_once('Net/SSH2.php');
include_once('Net/SFTP.php');

Ces lignes étaient incluses dans chaque fichier que j'exécutais. Lors de l'exécution des fichiers un par un, tout fonctionnait bien, mais lors de l'exécution de tous les fichiers ensemble, j'ai eu le problème de fuite de mémoire. D'une certaine manière, le "include_once" n'inclut pas les choses une fois, ou je fais quelque chose de mal ...

Omar Al-Azzawi
la source
set_include_path(get_include_path() . get_include_path().'/phpseclib'); Cela ajoutera le chemin '/ phpseclib' une fois pour chaque fichier qui a la ligne ... afin qu'il puisse l'ajouter plusieurs fois! Je suggère de le mettre dans un fichier de paramètres et include_oncele fichier de paramètres.
Farfromunique