„Allowed memory size of … bytes exhausted“ – jak naprawić błąd pamięci w PHP
PHP przerywa pracę z błędem „Allowed memory size of … bytes exhausted“? Pokazuję, jak podnieść memory_limit i jak rozpoznać, kiedy wyższy limit tylko ukrywa prawdziwy problem.
W skrócie: PHP pozwala każdemu wywołaniu skryptu zużyć tylko określoną ilość pamięci, ustawioną w parametrze memory_limit. Komunikat „Allowed memory size of … bytes exhausted“ oznacza, że skrypt chciał więcej. Doraźnie pomaga podniesienie limitu w php.ini, .user.ini, .htaccess albo – w WordPressie – przez WP_MEMORY_LIMIT. Jeśli jednak za błędem stoi nieskończona pętla, gigantyczne zapytanie do bazy albo za duże zdjęcie, wyższy limit tylko przesuwa problem w czasie.
Pełny komunikat wygląda zwykle tak:
Fatal error: Allowed memory size of 134217728 bytes exhausted
(tried to allocate 20480 bytes) in /var/www/html/includes/export.php on line 87
W WordPressie często widać tylko „Es gab einen kritischen Fehler auf dieser Website“ (błąd krytyczny). Właściwy komunikat jest wtedy w pliku debug.log albo w logu błędów na hostingu.
Jak czytać ten komunikat
Liczba po „Allowed memory size of“ to limit w bajtach. Po dwukrotnym podzieleniu przez 1024 wychodzą megabajty. Najczęstsze wartości:
67108864bajtów = 64 MB134217728bajtów = 128 MB268435456bajtów = 256 MB536870912bajtów = 512 MB
„Tried to allocate“ to tylko ostatni kawałek pamięci, o który PHP prosiło. Jeśli ta liczba jest mała (kilka kilobajtów), pamięć zapełniała się stopniowo – typowe dla pętli i dużych ilości danych. Jeśli jest ogromna (wiele megabajtów), PHP chciało naraz duży blok – typowe dla obrazów albo wczytywania całych plików.
Ważne: plik i linia z komunikatu pokazują, gdzie pamięć się skończyła. Niekoniecznie to miejsce ją zużyło. Jak z pełną szklanką – ostatnia kropla nie jest winna temu, że woda się wylała.
Najczęstsze przyczyny
Limit naprawdę jest za niski
Starsze pakiety hostingowe albo konfiguracje serwera mają czasem jeszcze 32 lub 64 MB. Dla współczesnego WordPressa z page builderem albo dla sklepu to mało. Tu podniesienie limitu jest właściwym rozwiązaniem.
Za dużo danych naraz
Skrypt ładuje całą tabelę do tablicy: wszystkie zamówienia, wszystkich klientów, wszystkie wpisy logu. Przy 2 000 rekordów działało, przy 200 000 już nie. W WordPressie klasyczny przykład to zapytanie z 'posts_per_page' => -1, które pobiera naraz wszystkie wpisy lub produkty. Funkcje eksportu też lubią budować cały plik w pamięci, zanim go wyślą.
Nieskończona pętla lub rekurencja
Funkcja wywołuje samą siebie bez końca albo pętla w kółko dopisuje dane do tablicy. W WordPressie dzieje się tak często przy hookach: funkcja podpięta pod save_post wywołuje wp_update_post(), które znowu uruchamia save_post – i tak dalej, aż zabraknie pamięci.
Duże zdjęcia
Przy zmniejszaniu obrazu PHP rozpakowuje go w całości do pamięci. Liczą się piksele, a nie rozmiar pliku. Zdjęcie 6000 × 4000 pikseli potrzebuje w GD, licząc z grubsza, około 100 MB – nawet jeśli plik JPG ma tylko 5 MB. Przy zdjęciach prosto ze smartfona czy aparatu 128 MB szybko przestaje wystarczać.
Pamięć rosnąca powoli
Długo działające skrypty, jak importy czy zadania cron, zbierają dane w cache albo zmiennych statycznych, których nikt nie czyści. Wychodzi to dopiero, gdy przejdzie przez nie odpowiednio dużo rekordów.
Jak podnieść memory_limit
Najpierw proszę sprawdzić, jaki limit obowiązuje teraz. W WordPressie jest to w „Werkzeuge → Website-Zustand → Bericht → Server“ (Narzędzia → Kondycja witryny → Informacje → Serwer). Poza WordPressem wystarczy mały plik z <?php phpinfo(); – wartość widać przy memory_limit. Ten plik trzeba potem usunąć, bo zdradza dużo o serwerze.
php.ini lub .user.ini
memory_limit = 256M
Na własnym serwerze ustawia się to w php.ini. Na hostingu współdzielonym zwykle w pliku .user.ini w katalogu głównym. Jest on czytany tylko wtedy, gdy PHP działa jako FastCGI lub PHP-FPM, a zmiany często zaczynają działać dopiero po kilku minutach. Wielu hostingodawców pozwala też ustawić tę wartość bezpośrednio w panelu klienta – to wtedy najprostsza droga.
.htaccess
php_value memory_limit 256M
Uwaga: to działa tylko wtedy, gdy PHP pracuje jako moduł Apache. Przy PHP-FPM albo FastCGI ta linia powoduje błąd „500 Internal Server Error“. Jeśli po zmianie strona w ogóle przestała działać, proszę od razu ją usunąć.
WordPress: WP_MEMORY_LIMIT
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
Te linie wpisuje się do wp-config.php, powyżej require_once ABSPATH . 'wp-settings.php';. WP_MEMORY_LIMIT dotyczy strony, a WP_MAX_MEMORY_LIMIT zadań w panelu, które potrzebują więcej pamięci. Domyślnie WordPress ustawia 40 MB (w Multisite 64 MB), ale podnosi limit tylko wtedy, gdy PHP pozwala na mniej – nigdy go nie obniża.
Każdy z tych sposobów ma granicę: jeśli hostingodawca ustawił maksimum, wyższa wartość nic nie da. Dlatego po zmianie warto sprawdzić, czy nowy limit faktycznie obowiązuje.
Dlaczego więcej pamięci często tylko ukrywa problem
Wyższy limit ustawia się w minutę i właśnie dlatego tak kusi. Ale warto zadać sobie pytanie: dlaczego zwykła podstrona nagle potrzebuje więcej niż 128 MB?
- Przy nieskończonej pętli nie pomoże żaden limit. Przy 512 MB błąd pojawi się tylko później, a w międzyczasie proces blokuje serwer.
- Przy za dużym zapytaniu zapotrzebowanie rośnie razem z danymi. Dziś wystarczy 256 MB, za rok przy dwa razy większej liczbie zamówień już nie. Błąd wróci, tylko później.
- Każde wywołanie strony, które zajmuje 300 MB, zajmuje je dla każdego odwiedzającego w tym samym momencie. Przy większym ruchu pamięć serwera się kończy, a strona zwalnia albo przestaje działać.
Wartość memory_limit = -1 (bez limitu) nie powinna więc nigdy trafić na działającą stronę. Wtedy nie przerywa się już jeden skrypt, tylko w najgorszym razie pada cały serwer.
Jak znaleźć przyczynę
- Ustalić moment: czy błąd pojawia się na każdej stronie, czy tylko przy konkretnej akcji – wgrywaniu plików, eksporcie, imporcie, zamówieniu?
- Sprawdzić wtyczki: w WordPressie wyłączać je po kolei, najlepiej na kopii strony. Wtyczka „Query Monitor“ pokazuje zużycie pamięci i zapytania do bazy dla każdej podstrony.
- Zmierzyć: we własnym kodzie pomaga prosty zapis do logu błędów w podejrzanych miejscach:
error_log( 'Pamiec: ' . round( memory_get_peak_usage( true ) / 1048576, 1 ) . ' MB' );
Jeśli wartość rośnie z każdym obiegiem pętli, miejsce jest znalezione.
Jak to naprawić porządnie
Duże ilości danych przetwarza się porcjami, a nie wszystkie naraz:
$offset = 0;
do {
$rows = $pdo->query(
"SELECT id, email FROM klienci ORDER BY id LIMIT 500 OFFSET $offset"
)->fetchAll();
foreach ( $rows as $row ) {
przetworz( $row );
}
$offset += 500;
} while ( count( $rows ) === 500 );
Eksport zapisuje się wiersz po wierszu bezpośrednio do wyjścia lub do pliku, zamiast składać go najpierw w całości. Przy hookach własny hook odpina się przed wywołaniem wp_update_post() i podpina z powrotem po nim. A zdjęcia najlepiej zmniejszyć do rozsądnego rozmiaru jeszcze przed wgraniem.
Czego unikać
- Podnoszenia limitu w nieskończoność bez pytania o przyczynę.
- Wpisywania
php_valuedo.htaccessbez wiedzy, jak PHP działa u danego hostingodawcy. - Ustawiania wartości w trzech miejscach naraz – potem nikt nie wie, która obowiązuje.
- Zostawiania pliku z
phpinfo()na serwerze.
Profesjonalna pomoc
Jeśli błąd wraca mimo wyższego limitu albo nie wiadomo, która wtyczka lub który fragment kodu zjada pamięć, znajdę to miejsce. Naprawa błędów PHP zaczyna się od analizy za 150 € netto, sama naprawa kosztuje od 290 € netto. Wysokie zużycie pamięci i wolne ładowanie strony mają często wspólną przyczynę. Jeśli strona jest też zauważalnie powolna, warto zajrzeć do oferty przyspieszenia strony – od 490 € netto.