Błąd krytyczny: dozwolony rozmiar pamięci 134217728 bajtów wyczerpanych (CodeIgniter + XML-RPC)

617

Mam kilka systemów POS, które okresowo wysyłają nowe dane sprzedażowe do jednej scentralizowanej bazy danych, która przechowuje dane w jednej dużej bazie danych w celu generowania raportów.

Klient POS oparty jest na PHPPOS, a ja wdrożyłem moduł, który używa standardowej biblioteki XML-RPC do wysyłania danych sprzedaży do usługi. System serwera jest zbudowany na CodeIgniter i wykorzystuje biblioteki XML-RPC i XML-RPCS dla komponentu webservice. Ilekroć wysyłam dużo danych dotyczących sprzedaży (zaledwie 50 wierszy z tabeli sprzedaży i poszczególnych wierszy z elementów_ sprzedaży dotyczących każdego elementu w sprzedaży), pojawia się następujący błąd:

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

128M jest wartością domyślną php.ini, ale zakładam, że jest to ogromna liczba do złamania. W rzeczywistości próbowałem nawet ustawić tę wartość na 1024 M, a wszystko, co robi, zajmuje więcej czasu, aby się pomylić.

Jeśli chodzi o kroki, które podjąłem, próbowałem wyłączyć wszystkie przetwarzanie po stronie serwera i przygotowałem je tak, aby zwróciło pełną odpowiedź niezależnie od danych wejściowych. Uważam jednak, że problem polega na faktycznym wysłaniu danych. Próbowałem nawet wyłączyć maksymalny czas wykonywania skryptu dla PHP i nadal występuje błąd.

ArcticZero
źródło
5
Jestem trochę zdezorientowany ... gdzie pojawia się błąd - na kliencie lub serwerze? A na którym etapie ... wysyłanie klientów, odbieranie serwerów, przetwarzanie serwerów, wysyłanie serwerów, odbieranie klientów lub przetwarzanie klientów?
Greg
2
Błąd wydaje się występować podczas wysyłania klienta lub odbierania serwera. Próbowałem wyłączyć wszystkie przetwarzanie po stronie serwera i ustawić je tak, aby wysyłało odpowiedź w puszce niezależnie od wysyłanych danych. Błąd występuje, jeśli prześlę określoną ilość danych. Zmieniam ustawienie PHP.ini.
ArcticZero
42
limit pamięci to 128 MB, podwoj to:ini_set('memory_limit', '256M');
9
Podsumowanie zanegowało wszystkie odpowiedzi „po prostu zignoruj ​​wyciek”, ludzi, którzy pomylili CodeIgniter z Drupalem oraz ludzi, którzy po prostu kopiują i wklejają odpowiedzi innych ludzi, aby zdobyć punkty. Jakość odpowiedzi w tym jest fatalna.
Matti Virkkunen,

Odpowiedzi:

697

Zmiana memory_limitBy ini_set('memory_limit', '-1');to nie właściwe rozwiązanie. Proszę nie rób tego.

Twój kod PHP może mieć gdzieś wyciek pamięci i mówisz serwerowi, aby używał całej pamięci, którą chce. W ogóle nie naprawiłbyś problemu. Jeśli monitorujesz serwer, zobaczysz, że prawdopodobnie zużywa on większość pamięci RAM, a nawet zamienia na dysk.

Prawdopodobnie powinieneś spróbować wyśledzić obrażający kod w swoim kodzie i go naprawić.

Jeff
źródło
173
@Jeff prawdopodobnie masz rację w 95% przypadków. Są jednak chwile, kiedy faktycznie potrzebujesz więcej pamięci. Załóżmy na przykład, że twoja aplikacja ładuje do pamięci ogromną ilość danych w celu przetworzenia (powiedzmy listę materiałów zawierającą 15 tys. Składników). Nie zawsze jest tak, że kod jest błędny, czasem potrzebujesz trochę więcej pamięci (np. 256 MB zamiast 128 MB). Zgadzam się jednak, że ustawienie jej na -1 jest strasznie złe. Ale dostosowanie limitu pamięci do rozsądnych sytuacji w czasie wykonywania jest całkowicie akceptowalnym imho.
Piryt
24
@pyrite tak masz rację, że czasami proces wymaga więcej pamięci, ale należy zwiększyć limit pamięci do jakiegoś logicznego kwoty jak 256 jak mówiłeś lub 512 czemu nie, ale nie 1;)
Lukas Lukac
9
@jeff W pełni się zgadzam, wartość -1może być użyteczna tylko w środowiskach programistycznych do celów testowych.
Esolitos
4
@Pyrite w przypadkach, które wskazałeś dla pozostałych 5%, odczytuje dane w porcjach i używa pracownika do przetworzenia ich zamiast zużywania większej ilości pamięci. To rozwiązanie będzie się skalować, a twoja sugestia nie będzie działać, z wyjątkiem tego, że w miarę upływu czasu będziesz ciągle wkładać do serwera coraz więcej pamięci.
burzum
2
Najczęściej ten problem w ORM, gdy próbujesz pobrać wszystkie dane, które znacznie więcej limitu pamięci php. Na przykład podczas próby generowania raportu miesięcznego.
Stepchik
213

ini_set('memory_limit', '-1');zastępuje domyślny limit pamięci PHP .

Chris Lane
źródło
16
@williamcarswell; -1jest wartością, którą PHP rozumie jako nieograniczoną w tym kontekście.
Alix Axel,
7
@ ArseniuszŁozicki - zużyje również zasoby, których serwer nie może oszczędzić.
Ken Williams
124
Szkoda, że ​​zyskuje tak wiele pozytywnych opinii. Ustawienie jej na dokładną wartość, za pomocą edycji php.ini lub ini_set, jest idealnie poprawnym rozwiązaniem, gdy ludzie potrzebują więcej pamięci. Ustawienie go na nieograniczony to niebezpieczny hack :(
Jeff Davis
24
@ użytkownik1767586 następnie ustaw go na rozsądną wartość. Możesz zapobiec zgłaszaniu błędu przez skrypt, ustawiając go na 1024M. Jeśli odpowiedź brzmi ini_set („memory_limit”, „1024M”); Możesz skopiować i wkleić to i być w porządku. Ustawiając go na -1, ustawiasz się na skrypt, który zużywa całą pamięć. Zwłaszcza jeśli robisz to rutynowo. Umieszczenie „niebezpiecznego” w cudzysłowie nie czyni go mniej niebezpiecznym. Naprawdę możesz podłączyć serwer hosta. Może zacznij niszczyć dane. Nie wiem, może stracić pracę? Brzmi dla mnie dość niebezpiecznie. : |
Jeff Davis
3
Przykro mi, że odpowiedź na +161 głosów i -3 głosów jest taka sama :(
akarthik10,
130

Prawidłowym sposobem jest edycja php.inipliku. Edytuj memory_limitdo pożądanej wartości.

Od twojego pytania 128M(który jest domyślnym limitem) zostało przekroczone, więc jest coś poważnie nie tak z twoim kodem, ponieważ nie powinien on zabierać tak dużo.

Jeśli wiesz, dlaczego to tyle zajmuje, i chcesz pozwolić, by ustawiono go memory_limit = 512Mwyżej lub wyżej i powinieneś być dobry.

Basav
źródło
7
Szczerze mówiąc, jeśli buforujesz jakieś poważne ilości danych, to jest poprawna odpowiedź. 128M to za mało dla niektórych skryptów. 512M lub 1024M często wystarczą, ale musisz decydować indywidualnie.
Jeff Davis,
2
Yeha, jednak staraj się unikać
dużego
2
limit_pamięci = -1; ustawiony w php.ini
2
@YumYumYum Usuwa limit_pamięci, którego chcesz tylko, jeśli monitorujesz użycie pamięci w inny sposób. System operacyjny zabije proces, jeśli w jakikolwiek sposób zajmie ogromną ilość pamięci.
Flimm,
Jeśli więc uruchamiasz skrypt, który zużywa dużo pamięci, ale musisz go uruchomić tylko raz, czy możesz po prostu zwiększyć limit pamięci dla procesu w momencie wykonania, a następnie obniżyć limit pamięci ponownie po jednorazowym skrypt działa?
chromechris
95

Przydział pamięci dla PHP można zmienić na stałe lub tymczasowo.

Na stałe

Możesz trwale zmienić przydział pamięci PHP na dwa sposoby.

Jeśli masz dostęp do swojego php.inipliku, możesz edytować wartość dlamemory_limit żądanej wartości.

Jeśli nie masz dostępu do swojego php.inipliku (a webhost na to pozwala), możesz zastąpić przydział pamięci za pomocą swojego .htaccesspliku. Dodaj php_value memory_limit 128M(lub cokolwiek chcesz).

Chwilowy

Możesz dostosować przydział pamięci w locie z poziomu pliku PHP. Po prostu masz kod ini_set('memory_limit', '128M');(lub jakikolwiek inny pożądany przydział). Możesz usunąć limit pamięci (chociaż limity komputera lub instancji mogą nadal obowiązywać), ustawiając wartość na „-1”.

Umair Idrees
źródło
2
Dziękuję, nie pomyślałem, aby sprawdzić, czy ktoś ustawił wartość w .htaccess, która przesłaniała php.ini i nie mogłem zrozumieć, dlaczego +1
HostMyBus
61

Bardzo łatwo jest uzyskać wycieki pamięci w skrypcie PHP - szczególnie jeśli używasz abstrakcji, takiej jak ORM. Spróbuj użyć Xdebug do profilowania skryptu i dowiedz się, dokąd poszła cała ta pamięć.

troelskn
źródło
1
Pójdę wypróbować Xdebug. Nigdy wcześniej go nie używałem, więc muszę o tym przeczytać. Dziękuję za odpowiedź! Mam nadzieję, że wkrótce znajdę odpowiedź na to pytanie
ArcticZero,
34
Pamiętaj, że PHP wykorzystuje liczenie referencji do zarządzania pamięcią. Więc jeśli masz odwołania cykliczne lub zmienne globalne, te obiekty nie zostaną poddane recyklingowi. Jest to zwykle źródło wycieków pamięci w PHP.
troelskn
Xdebug pokazuje, że biblioteka Xmlrpc.php CI jest odpowiedzialna za mój wyciek pamięci. Czy przypadkiem będą jakieś problemy z bibliotekami XML-RPC CodeIgniter, o których powinienem wiedzieć? Próbowałem wyłączyć wszystkie przetwarzanie po stronie serwera i nadal brakuje pamięci, jeśli podam wystarczającą ilość danych.
ArcticZero
1
Nie znam / nie używam CI, więc nie wiem. Ale prawdopodobnie powinieneś spróbować znaleźć obiekt, który nie jest zwalniany po użyciu - najprawdopodobniej z powodu cyklicznego odwołania. To praca detektywistyczna.
troelskn
1
To jedyna odpowiedź, która sugeruje rozwiązanie problemu. Inne odpowiedzi pobudzają pamięć do bandażowania symptomu i ignorowania choroby .
Chris Baker
56

Dodając 22,5 miliona rekordów do tablicy za pomocą array_push, ciągle dochodziło do „wyczerpania pamięci” krytycznych błędów przy około 20 milionach rekordów, wykorzystując 4Gjako limit pamięci w pliku php.ini. Aby to naprawić, dodałem oświadczenie

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

u góry pliku. Teraz wszystko działa dobrze. Nie wiem, czy PHP ma wyciek pamięci. To nie jest moja praca, nie obchodzi mnie to. Muszę tylko wykonać swoją pracę i to zadziałało.

Program jest bardzo prosty:

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

Błąd krytyczny wskazuje na linię 3, dopóki nie zwiększyłem limitu pamięci, co wyeliminowało błąd.

JamesAD-0
źródło
10
masz na myśli ini_set('memory_limit', '8192M');?
Gogol
2
Jakiż to luksus, mieć czas na optymalizację scenariusza dla czegoś takiego. Lub badaj i porównuj i ucz się narzędzi ETL lub niektórych takich. W prawdziwym świecie zwiększamy limit pamięci, robimy to i idziemy dalej.
Matthew Poer,
45

Wciąż otrzymywałem ten błąd, nawet po memory_limitustawieniu php.ini, a wartość poprawnie odczytywana za pomocą phpinfo().

Zmieniając to z tego:

memory_limit=4G

Do tego:

memory_limit=4096M

To rozwiązało problem w PHP 7.

Danny Beckett
źródło
23

Gdy zobaczysz powyższy błąd - zwłaszcza jeśli (tried to allocate __ bytes)jest to niska wartość, może to być wskaźnik nieskończonej pętli, jak funkcja, która wywołuje się bez wyjścia:

function exhaustYourBytes()
{
    return exhaustYourBytes();
}
Kristen Waite
źródło
19

Po włączeniu tych dwóch linii zaczął działać:

; 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
źródło
19

Możesz to naprawić, zmieniając memory_limitna fastcgi / fpm:

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

Zmień pamięć, na przykład z 128 na 512, patrz poniżej

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

do

; Maximum amount of memory a script may consume (128 MB)
; http://php.net/memory-limit
memory_limit = 512M
Derick Fynn
źródło
18

Katalog główny Twojej witryny:

ini_set('memory_limit', '1024M');
Gaurang P.
źródło
1
to zadziałało dla mnie. uwielbiam rozwiązania jednej linii. +1 za prostotę
Steve C
14

Zmień limit pamięci w pliku php.ini i zrestartuj Apache. Po ponownym uruchomieniu uruchom phpinfo (); funkcja z dowolnego pliku PHP w celu memory_limitpotwierdzenia zmiany.

memory_limit = -1

Limit pamięci -1 oznacza, że ​​nie ma ustawionego limitu pamięci. Teraz jest na maksimum.

Hasib Kamal
źródło
13

Dla użytkowników Drupala odpowiedź Chrisa Lane'a na:

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

działa, ale musimy go umieścić tuż po otwarciu

<?php

znacznik w pliku index.php w katalogu głównym witryny.

sigmapi13
źródło
13

W Drupal 7 możesz modyfikować limit pamięci w pliku settings.php znajdującym się w twoim folderze strony / domyślny. Wokół linii 260 zobaczysz to:

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

Nawet jeśli twoje ustawienia php.ini są wystarczająco wysokie, nie będziesz w stanie zużyć więcej niż 128 MB, jeśli nie jest to ustawione w pliku Drupal settings.php.

LK7889
źródło
1
Nie w Drupal7 nie ma takiego ciągu kodu w settings.php
FLY,
Nie ma również ciągu znaków w pliku settings.php dla drupala 6
AllisonC
12

Zamiast zmieniać memory_limitwartość w php.inipliku, jeśli jest część kodu, która mogłaby zużyć dużo pamięci, możesz usunąć memory_limitprzed uruchomieniem tej sekcji, a następnie zastąpić ją później.

$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
mykeels
źródło
7

PHP 5.3+ pozwala zmienić limit pamięci poprzez umieszczenie .user.inipliku w public_htmlfolderze. Po prostu utwórz powyższy plik i wpisz w nim następujący wiersz:

memory_limit = 64M

Niektóre hosty cPanel akceptują tylko tę metodę.

Sabi
źródło
7

Awaria strony?

Wpisz opis zdjęcia tutaj

(Dzieje się tak, gdy MySQL musi wyszukiwać duże wiersze. Domyślnie memory_limit jest ustawiony na mały, co było bezpieczniejsze dla sprzętu.)

Możesz sprawdzić status pamięci w systemie, zanim zwiększysz 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

Tutaj zwiększyłem go, jak poniżej, a następnie zrobiłem, service httpd restartaby naprawić problem ze stroną awarii.

# grep memory_limit /etc/php.ini
memory_limit = 512M
Peter Mortensen
źródło
Na jaką liczbę (wiersz i kolumnę?) Należy spojrzeć po uruchomieniu free -mpolecenia, aby zdecydować o nowym limicie pamięci?
kiradotee
7

Po prostu dodaj ini_set('memory_limit', '-1'); wiersz u góry strony internetowej.

I możesz ustawić swoją pamięć według potrzeb zamiast -1 16M, itd.

Pankaj Pratik Rai
źródło
6
To wydaje się mówić to samo, co wiele istniejących odpowiedzi. Najlepiej jest dodać odpowiedź na popularne pytanie tylko wtedy, gdy nowy materiał oferuje coś nowego.
halfer
6

Dla tych, którzy drapią się po głowie, aby dowiedzieć się, dlaczego na ziemi ta mała funkcja powinna powodować wyciek pamięci, czasem przez pomyłkę, funkcja zaczyna się nazywać rekurencyjnie na zawsze.

Na przykład klasa proxy, która ma taką samą nazwę dla funkcji obiektu, który będzie ją proxy.

class Proxy {

    private $actualObject;

    public function doSomething() {

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

Czasami możesz zapomnieć o zabraniu tego małego faktycznego członkaObjec, a ponieważ proxy ma taką doSomethingmetodę, PHP nie dałoby ci żadnego błędu, a dla dużej klasy można go ukryć przed oczami na kilka minut, aby dowiedzieć się, dlaczego przecieka pamięć.

madz
źródło
I kolejna wskazówka: możesz wprowadzić die('here')kod i przesunąć tę instrukcję, aby zobaczyć, gdzie zaczyna się rekurencja.
toddmo
6

Wystąpił błąd poniżej podczas pracy na zestawie danych mniejszym niż wcześniej działał.

Błąd krytyczny: wyczerpany dozwolony rozmiar pamięci 134217728 bajtów (próbował przydzielić 4096 bajtów) w C: \ workspace \ image_management.php w linii 173

Gdy przyniosło mi to poszukiwanie winy, pomyślałem, że wspomnę, że nie zawsze są to rozwiązania techniczne z poprzednich odpowiedzi, ale coś prostszego. W moim przypadku był to Firefox. Przed uruchomieniem program zużywał już 1157 MB.

Okazuje się, że przez kilka dni oglądałem po 50 minut wideo po trochu i to wszystko popsuło. Jest to rodzaj poprawki, którą eksperci poprawiają, nawet o tym nie myśląc, ale dla takich jak ja warto o tym pamiętać.

M61Vulcan
źródło
Miałem podobne wystąpienie w Google Chrome dzisiaj. Byłem bardzo sceptycznie nastawiony do tej odpowiedzi ... jednak ujawniło to, że moje wyczerpanie bajtów zniknęło po otwarciu okna incognito i ponownym uruchomieniu tego samego skryptu! Badania trwają.
mickmackusa
2

Uruchomienie takiego skryptu (na przykład przypadek cron): php5 /pathToScript/info.phppowoduje ten sam błąd.

Prawidłowy sposób: php5 -cli /pathToScript/info.php

ratm
źródło
2

Jeśli korzystasz z VPS (wirtualnego prywatnego serwera) zasilanego przez WHM, może się okazać, że nie masz uprawnień do bezpośredniej edycji PHP.INI; system musi to zrobić. W panelu sterowania hosta WHM przejdź do Konfiguracja usługiEdytor konfiguracji PHP i zmodyfikuj memory_limit:

Aktualizacja limitu pamięci na WHM 11.48.4

Janckos
źródło
2

Uważam, że jest to przydatne, gdy dołączam lub wymagam _dbconnection.php_oraz _functions.phpw faktycznie przetwarzanych plikach, a nie w nagłówku. Który jest zawarty w sobie.

Jeśli więc uwzględniono nagłówek i stopkę , po prostu dołącz wszystkie pliki funkcjonalne przed dołączeniem nagłówka.

Kerim
źródło
2

Używanie yieldmoże być również rozwiązaniem. Zobacz składnię generatora .

Zamiast zmieniać PHP.iniplik na większą pamięć, czasem zaimplementowanie yieldwewnętrznej pętli może rozwiązać problem. Wydajność polega na tym, że zamiast zrzucać wszystkie dane naraz, odczytuje je jeden po drugim, oszczędzając dużo pamięci.

Łukasza
źródło
2
PHP.ini? Nie jest to php.ini?
Peter Mortensen
1

Ten błąd jest czasami spowodowany błędem w kodzie PHP, który powoduje rekurencje związane z obsługą wyjątków i ewentualnie innymi operacjami. Niestety nie udało mi się stworzyć małego przykładu.

W tych przypadkach, które zdarzyły mi się kilka razy, set_time_limit kończy się niepowodzeniem, a przeglądarka próbuje załadować dane wyjściowe PHP, albo za pomocą nieskończonej pętli, albo za pomocą fatalnego komunikatu o błędzie, który jest tematem tego pytania.

Poprzez zmniejszenie dozwolonego rozmiaru alokacji poprzez dodanie

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

na początku kodu powinieneś być w stanie zapobiec błędowi krytycznemu.

Wtedy możesz zostać z programem, który się kończy, ale nadal jest trudny do debugowania.

W tym momencie wstaw BreakLoop()wywołania do swojego programu, aby przejąć kontrolę i dowiedzieć się, która pętla lub rekurencja w twoim programie powoduje problem.

Definicja BreakLoop jest następująca:

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

Argument $ LoopSite może być nazwą funkcji w kodzie. Nie jest to naprawdę konieczne, ponieważ komunikat o błędzie, który otrzymasz, wskaże Ci wiersz zawierający wywołanie BreakLoop ().

David Spector
źródło
-1

W moim przypadku był to krótki problem ze sposobem napisania funkcji. Wyciek pamięci może być spowodowany przez przypisanie nowej wartości do zmiennej wejściowej funkcji, np .:

/**
* 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';
}
Dmitrij Krawczuk
źródło
-6

Kiedy usunąłem następujące wiersze z mojego kodu, wszystko działało OK!

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

Te linie były zawarte w każdym pliku, który uruchomiłem. Podczas uruchamiania plików jeden po drugim wszystko działało OK, ale podczas uruchamiania wszystkich plików wystąpił problem z wyciekiem pamięci. Jakoś „include_once” nie obejmuje rzeczy raz, lub robię coś złego…

Omar Al-Azzawi
źródło
set_include_path(get_include_path() . get_include_path().'/phpseclib'); Spowoduje to dodanie ścieżki „/ phpseclib” jeden raz dla każdego pliku, który ma linię ... dzięki czemu może dodać ją wiele razy! Sugeruję umieszczenie go w pliku ustawień i include_oncepliku ustawień.
Farfromunique