J'ai un tas de systèmes de points de vente clients qui envoient périodiquement de nouvelles données de ventes à 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 point de vente du client est basé sur PHPPOS et j'ai implémenté un module qui utilise la bibliothèque standard XML-RPC pour envoyer des données de vente au service. Le système serveur est basé sur CodeIgniter et utilise les bibliothèques XML-RPC et XML-RPCS pour le composant webservice. Chaque fois que j'envoie un grand nombre de données de vente (aussi peu que 50 lignes de la table sales et des lignes individuelles de sales_items relatives à chaque article de la vente), l'erreur suivante apparaît:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 54 bytes)
128M est la valeur par défaut dans php.ini
, mais je suppose que c'est un nombre énorme à rompre. En fait, j'ai même essayé de définir cette valeur sur 1024M, mais le délai d'erreur est plus long.
En ce qui concerne les mesures que j'ai prises, j'ai essayé de désactiver tout le traitement côté serveur et je l'ai truqué pour renvoyer une réponse prédéfinie quelle que soit l'entrée. Cependant, je pense que le problème réside dans l'envoi effectif des données. J'ai même essayé de désactiver la durée maximale d'exécution du script pour PHP, mais il y a toujours erreur.
Changer le memory_limit
par ini_set('memory_limit', '-1');
est pas une solution appropriée. S'il vous plaît ne faites pas ça.
Votre code PHP peut avoir une fuite de mémoire quelque part et vous indiquez au serveur de n'utiliser que 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 majeure partie de la RAM et même le permutation sur le disque.
Vous devriez probablement essayer de localiser le code incriminé dans votre code et de le corriger.
ini_set('memory_limit', '-1');
remplace la valeur par défaut limite de mémoire PHP .
La bonne façon consiste à éditer votre fichier php.ini
. Éditez memory_limit
à la valeur désirée.
A partir de votre question, 128M
(qui est la limite par défaut) a été dépassé, il y a donc quelque chose qui cloche dans votre code car il ne devrait pas en prendre autant.
Si vous savez pourquoi cela prend autant et que vous voulez le laisser régler memory_limit = 512M
ou plus, vous devriez être bon.
L'allocation de mémoire pour PHP peut être ajustée de manière permanente ou temporaire.
Vous pouvez modifier de manière définitive l'allocation de mémoire PHP de deux manières.
Si vous avez accès à votre fichier php.ini
, vous pouvez modifier la valeur de memory_limit
à la valeur souhaitée.
Si vous n'avez pas accès à votre fichier php.ini
(et que votre hébergeur le permet), vous pouvez remplacer l'allocation de mémoire par l'intermédiaire de votre fichier .htaccess
. Ajoutez php_value memory_limit 128M
(ou quelle que soit l'allocation souhaitée).
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 l'allocation souhaitée). Vous pouvez supprimer la limite de mémoire (bien que des limites d'ordinateur ou d'instance puissent toujours s'appliquer) en définissant la valeur sur "-1".
Il est très facile d’obtenir des fuites de mémoire dans un script PHP, en particulier si vous utilisez une abstraction, telle qu’un ORM. Essayez d’utiliser Xdebug pour profiler votre script et découvrir où est passée toute cette mémoire.
Lors de l'ajout de 22,5 millions d'enregistrements dans un tableau avec array_Push, je continuais à avoir des erreurs fatales "de mémoire épuisée" à environ 20 millions d'enregistrements en utilisant 4G
comme 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, 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.
J'ai continué à avoir cette erreur, même avec memory_limit
défini dans php.ini
, et la valeur lue 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.
Lorsque vous voyez l'erreur ci-dessus - en particulier si le (tried to allocate __ bytes)
est une valeur basse, cela pourrait indiquer une boucle infinie, comme une fonction qui s'appelle sans issue:
function exhaustYourBytes()
{
return exhaustYourBytes();
}
Vous pouvez résoudre ce problème en modifiant memory_limit
sur fastcgi/fpm:
$vim /etc/php5/fpm/php.ini
Changer 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
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
répertoire racine de votre site:
ini_set('memory_limit', '1024M');
Pour Drupal utilisateurs _, cette réponse de Chris Lane est la suivante:
ini_set('memory_limit', '-1');
fonctionne, mais nous devons le mettre juste après l'ouverture
<?php
balise dans le fichier index.php du répertoire racine de votre site.
Plutôt que de modifier la valeur memory_limit
de votre fichier php.ini
, si une partie de votre code pouvait utiliser beaucoup de mémoire, vous pouvez supprimer le memory_limit
avant l'exécution de cette section, puis remplacez-le après.
$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
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 utiliser plus de 128 Mo si cela n'est pas défini dans votre fichier Drupal settings.php.
Changez la limite de mémoire dans le fichier php.ini et redémarrez Apache. Après le redémarrage, exécutez la fonction phpinfo (); à partir de n’importe quel fichier PHP pour une confirmation de modification de memory_limit
.
memory_limit = -1
Limite de mémoire -1 signifie qu'il n'y a pas de limite de mémoire définie. C'est maintenant au maximum.
Page de blocage?
(Cela se produit lorsque MySQL doit interroger de grandes lignes. Par défaut, memory_limit
est défini sur small, 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’augmente comme ci-dessous, puis fais service httpd restart
pour résoudre le problème de la page de blocage.
# grep memory_limit /etc/php.ini
memory_limit = 512M
PHP 5.3+ vous permet de modifier la limite de mémoire en plaçant un fichier .user.ini
dans le dossier public_html
. Créez simplement le fichier ci-dessus et tapez la ligne suivante:
memory_limit = 64M
Certains hôtes cPanel n'acceptent que cette méthode.
Pour ceux qui se grattent la tête pour découvrir pourquoi, sur terre, cette petite fonction devrait provoquer une fuite de mémoire, parfois par une petite erreur, une fonction commence à s’appeler de manière récursive à jamais.
Par exemple, une classe proxy qui porte le même nom pour une fonction de l'objet qui va le proxy.
class Proxy {
private $actualObject;
public function doSomething() {
return $this->actualObjec->doSomething();
}
}
Parfois, vous pouvez oublier d’apporter ce petit membre de actualObjec et, comme le proxy utilise en fait cette méthode doSomething
, PHP, il ne vous donnerait aucune erreur et, pour une classe importante, il pourrait être masqué du yeux pendant quelques minutes pour savoir pourquoi il fuit la mémoire.
J'ai eu l'erreur ci-dessous lors de l'exécution sur un ensemble de données plus petit que celui qui avait fonctionné auparavant.
Erreur irrécupérable: taille de mémoire autorisée de 134217728 octets épuisés (tentative d'allocation de 4096 octets) dans C:\workspace\image_management.php à la ligne 173
La recherche de la faute m'ayant amené ici, j'ai pensé mentionner que ce ne sont pas toujours les solutions techniques des réponses précédentes, mais quelque chose de plus simple. Dans mon cas, c'était Firefox. Avant de lancer le programme, il utilisait déjà 1 157 Mo.
Il s’avère que j’ai visionné une vidéo de 50 minutes un peu à la fois pendant plusieurs jours, ce qui a tout gâché. C'est le genre de solution que les experts corrigent sans même y penser, mais pour ceux comme moi, il ne faut pas oublier.
Si vous utilisez un VPS (serveur privé virtuel) basé sur WHM, vous constaterez peut-être que vous ne disposez pas des autorisations nécessaires pour modifier PHP.INI directement. 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
:
L'exécution du script comme ceci (cas de casse-croûte par exemple): php5 /pathToScript/info.php
produit la même erreur.
La bonne façon: php5 -cli /pathToScript/info.php
Je trouve cela utile d’inclure ou d’exiger _dbconnection.php_
et _functions.php
dans des fichiers réellement traités, plutôt que de les 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.
Cette erreur est parfois due à 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 été capable de créer un petit exemple.
Dans ces cas, qui se sont produits plusieurs fois pour moi, 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');
au début de votre code, vous devriez pouvoir éviter l’erreur fatale.
Ensuite, vous pouvez vous retrouver avec un programme qui se termine, mais reste difficile à déboguer.
À ce stade, insérez BreakLoop()
dans votre programme pour prendre le contrôle et déterminer la boucle ou la récursivité à 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 ().
Utiliser yield
pourrait également être une solution. Voir syntaxe du générateur.
Au lieu de changer le fichier PHP.ini
pour un stockage en mémoire plus important, parfois, implémenter un yield
dans une boucle pourrait résoudre le problème. Au lieu de vider toutes les données en une seule fois, le rendement est lu, une par une, ce qui permet d’économiser beaucoup de mémoire.
Dans mon cas, la manière dont une fonction était écrite posait un problème bref. Une fuite de mémoire peut être provoquée en attribuant 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';
}
Ajoutez simplement ini_set ('memory_limit', '-1'); ligne en haut de votre page Web. Et vous pouvez régler votre mémoire selon vos besoins à la place de -1 à 16M, etc.