Вступление: почему ваш PHP-скрипт «взрывается»?
Вы когда-нибудь видели на экране белое полотно с леденящей душу надписью «Fatal Error: Allowed Memory Size of 134217728 Bytes Exhausted»? Если да — вы не одиноки. Этот вопрос на Stack Overflow собрал тысячи просмотров, и каждый второй PHP-разработчик хотя бы раз в жизни с ним сталкивался.
Цифра 134217728 байт — это ровно 128 мегабайт (стандартный лимит для многих хостингов). Когда ваш скрипт пытается занять больше — PHP говорит «стоп» и выбрасывает фатальную ошибку. Но не спешите паниковать! В этой статье мы разберём, почему это происходит, как исправить и — самое главное — как оптимизировать производительность, чтобы ошибка памяти больше вас не тревожила.
Статья написана для образовательного сайта PythonLib, но весь код и примеры — на PHP. Поехали!
Что такое memory_limit и откуда берётся «134217728»?
В PHP есть директива memory_limit, которая устанавливает максимальный объём памяти, доступный одному скрипту. По умолчанию во многих конфигурациях (особенно на shared-хостингах) стоит значение 128M (или 134217728 байт).
Проверить текущий лимит можно так:
<?php
echo ini_get('memory_limit'); // Выведет, например, "128M"
?>
Когда ваш скрипт превышает этот лимит — PHP немедленно завершает выполнение с фатальной ошибкой. Причины могут быть разными: от неоптимального кода до загрузки гигантских файлов в память.
Типичные сценарии, вызывающие ошибку
- Работа с большими массивами — например, чтение CSV-файла целиком в память.
- Циклы с утечками — забыли освободить переменные или используете глобальные массивы.
- Библиотеки для обработки изображений — GD или Imagick при работе с фото высокого разрешения.
- Рекурсивные функции — без базового условия уходят в бесконечность и съедают стек.
- ORM и кэширование — некоторые ORM (например, Doctrine) могут загружать всю таблицу в память.
Как исправить ошибку: 3 проверенных способа
1. Временное увеличение лимита через ini_set()
Самый быстрый способ — прямо в скрипте увеличить лимит:
<?php
ini_set('memory_limit', '256M');
// Теперь скрипт может использовать до 256 MB
?>
Важно: это сработает только если ваш хостинг разрешает переопределение директивы. На некоторых хостингах ini_set() для memory_limit заблокирован.
2. Изменение php.ini или .htaccess
Если у вас есть доступ к серверу, отредактируйте php.ini:
memory_limit = 256M
Или через .htaccess (на Apache):
php_value memory_limit 256M
После изменений перезапустите веб-сервер или PHP-FPM.
3. Оптимизация кода — лучший способ
Увеличить лимит — это костыль. Настоящее решение — написать эффективный код. Давайте разберём примеры.
Реальный пример: обработка большого CSV-файла
Допустим, у вас есть CSV-файл на 500 000 строк. Наивный код может выглядеть так:
<?php
$rows = file('data.csv'); // Загружает ВЕСЬ файл в память!
foreach ($rows as $row) {
// обработка
}
?>
При размере файла 200 MB — скрипт упадёт с ошибкой памяти.
Оптимизация: построчное чтение
<?php
$handle = fopen('data.csv', 'r');
if ($handle) {
while (($row = fgetcsv($handle)) !== false) {
// обрабатываем одну строку за раз
// память почти не растёт
}
fclose($handle);
}
?>
Теперь скрипт использует всего несколько мегабайт, независимо от размера файла.
Профилирование памяти: находим «прожорливые» участки
Чтобы понять, где именно тратится память, используйте встроенные функции PHP:
memory_get_usage()— текущее потребление памяти.memory_get_peak_usage()— пиковое потребление за время работы скрипта.
Пример профилирования:
<?php
echo "Начало: " . memory_get_usage() . " bytes\n";
$bigArray = range(1, 100000);
echo "После создания массива: " . memory_get_usage() . " bytes\n";
unset($bigArray);
echo "После очистки: " . memory_get_usage() . " bytes\n";
echo "Пик: " . memory_get_peak_usage() . " bytes\n";
?>
Вывод поможет увидеть, какие операции «съедают» память. Если пик близок к лимиту — ищите утечки или неоптимальные структуры данных.
Использование Xdebug для глубокого анализа
Для серьёзного профилирования установите Xdebug и включите xdebug.mode=profile. Тогда вы получите детальный отчёт в виде flame graph, где видно, сколько памяти и времени тратит каждая функция.
Типичные ошибки и их решения
Ошибка: загрузка изображений через GD
При работе с фото 6000x4000 пикселей библиотека GD может запросить десятки мегабайт. Решение — уменьшать разрешение или использовать Imagick с оптимизацией.
<?php
// Плохо
$img = imagecreatefromjpeg('large.jpg');
// Хорошо — сразу уменьшаем
$src = imagecreatefromjpeg('large.jpg');
$dst = imagecreatetruecolor(800, 600);
imagecopyresampled($dst, $src, 0, 0, 0, 0, 800, 600, imagesx($src), imagesy($src));
imagedestroy($src); // освобождаем исходник
?>
Ошибка: рекурсия без ограничения
<?php
function recursive($n) {
if ($n <= 0) return;
recursive($n - 1);
}
recursive(100000); // Fatal Error! Стек переполнен
?>
Решение — увеличить xdebug.max_nesting_level или переписать алгоритм итеративно.
Продвинутые техники оптимизации
1. Использование генераторов (yield)
Генераторы позволя