Неустранимая ошибка: допустимый объем памяти 134217728 байт исчерпан (CodeIgniter + XML-RPC)

619

У меня есть несколько систем POS, которые периодически отправляют новые данные о продажах в одну централизованную базу данных, которая сохраняет эти данные в одной большой базе данных для генерации отчетов.

Клиент POS основан на PHPPOS, и я реализовал модуль, который использует стандартную библиотеку XML-RPC для отправки данных о продажах в сервис. Серверная система построена на CodeIgniter и использует библиотеки XML-RPC и XML-RPCS для компонента веб-сервиса. Всякий раз, когда я отправляю много данных о продажах (всего 50 строк из таблицы продаж и отдельные строки из sales_items, относящиеся к каждому товару в продаже), я получаю следующую ошибку:

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

128M - это значение по умолчанию php.ini, но я предполагаю, что это огромное число, которое нужно сломать. На самом деле, я даже пытался установить это значение на 1024M, и все, что он делает, это занимает больше времени, чтобы вывести ошибку.

Что касается шагов, которые я предпринял, я попытался отключить всю обработку на стороне сервера и настроил ее так, чтобы она возвращала стандартный ответ независимо от ввода. Тем не менее, я считаю, что проблема заключается в фактической отправке данных. Я даже пытался отключить максимальное время выполнения скрипта для PHP, и он все еще выдает ошибки.

ArcticZero
источник
5
Я немного запутался ... где возникает ошибка - на клиенте или сервере? И на каком этапе ... отправка клиента, получение сервера, обработка сервера, отправка сервера, получение клиента или обработка клиента?
Грег
2
Кажется, ошибка возникает либо во время отправки клиента, либо при получении сервером. Я попытался отключить всю обработку на стороне сервера и настроить ее на отправку готового ответа независимо от отправленных данных. Ошибка возникает, если я отправляю определенное количество данных. Я изменяю настройку PHP.ini.
ArcticZero
42
ограничение памяти составляет 128 МБ, удвойте его:ini_set('memory_limit', '256M');
9
Резюме опровергло все ответы «просто игнорируйте утечки», людей, которые путали CodeIgniter с Drupal, и людей, которые просто копировали и вставляли ответы других людей, чтобы получить очки. Качество ответов в этом вопросе ужасно.
Матти Вирккунен

Ответы:

697

Изменение memory_limitВ ini_set('memory_limit', '-1');является не правильным решением. Пожалуйста, не делай этого.

Ваш PHP-код может иметь утечку памяти где-то, и вы говорите серверу просто использовать всю необходимую память. Вы бы не решили проблему вообще. Если вы проконтролируете свой сервер, вы увидите, что он теперь, вероятно, использует большую часть оперативной памяти и даже подключается к диску.

Вероятно, вам следует попытаться отследить код в вашем коде и исправить его.

Джефф
источник
174
@ Джефф, наверное, ты прав в 95% случаев. Однако бывают случаи, когда вам действительно нужно больше памяти. Например, допустим, ваше приложение загружает огромное количество данных в память для обработки (например, спецификация с 15k компонентами). Это не всегда тот случай, когда код содержит ошибки, иногда вам просто нужно немного больше памяти (например, 256M вместо 128M). Однако я согласен, что установка его в -1 ужасно плохо. Но настройка предела памяти для разумных ситуаций во время выполнения вполне приемлема imho.
Пирит
24
@pyrite да, вы правы, что иногда процессу требуется больше памяти, но вы должны увеличить предел памяти до некоторого логического объема, например, 256 МБ, как вы сказали, или 512 МБ, почему бы не НО-1;)
Лукас Лукач
9
@jeff Я полностью согласен, что значение -1может быть полезно только в средах разработки для тестирования.
Esolitos
4
@ Пирит в случаях, которые вы назвали для оставшихся 5%, считывает данные порциями и использует рабочий для их обработки, вместо того, чтобы использовать больше памяти. Это решение будет также масштабироваться, пока ваше предложение не будет работать, за исключением того, что вы продолжаете загружать все больше и больше памяти на ваш сервер с течением времени, если данные растут.
Burzum
2
В большинстве случаев это проблема в ORM, когда вы пытаетесь получить все данные, которые намного больше ограничения памяти php. Например, когда вы пытаетесь создать ежемесячный отчет.
Степчик
213

ini_set('memory_limit', '-1');переопределяет ограничение памяти PHP по умолчанию .

Крис Лейн
источник
16
@williamcarswell; -1это значение, которое PHP понимает как неограниченное в этом контексте.
Аликс Аксель
7
@ ArseniuszŁozicki - он также потребляет ресурсы, которые сервер не может сэкономить.
Кен Уильямс
124
Позор, что это получает так много голосов. Установка его на точное значение, с помощью php.ini edits или ini_set, является совершенно правильным решением, когда людям нужно больше памяти. Установка неограниченного количества - опасный хак :(
Джефф Дэвис,
24
@ user1767586 затем установите для него нормальное значение. Вы можете предотвратить выдачу ошибки сценарием, установив его в 1024M. Если этот ответ сказал ini_set ('memory_limit', '1024M'); Вы можете скопировать и вставить это и все будет в порядке. Установив его на -1, вы настраиваете себя на создание сценария, который потребляет всю память. Особенно, если вы делаете это регулярно. Помещение «опасно» в кавычки не делает его менее опасным. Вы действительно можете подключить свой хост-сервер. Может быть, начать уничтожать данные. Не знаю, может потерять работу? Звучит довольно опасно для меня. : |
Джефф Дэвис
3
Грустно видеть, что ответ для +161 голосов и -3 голосов одинаков :(
akarthik10
130

Правильный способ - отредактировать ваш php.iniфайл. Отредактируйте memory_limitпо вашему желанию значение.

Как видно из вашего вопроса, 128M(который является пределом по умолчанию) был превышен, поэтому в вашем коде что-то серьезно не так, так как он не должен занимать так много времени.

Если вы знаете, почему это занимает так много времени, и вы хотите, чтобы он был установлен memory_limit = 512Mили выше, и вы должны быть хорошими.

Basav
источник
7
Честно говоря, если вы кешируете какие-то серьезные объемы данных, это правильный ответ. 128M недостаточно для определенных сценариев. 512M или 1024M часто будет достаточно, но вы должны решить в каждом конкретном случае.
Джефф Дэвис
2
Да, однако старайтесь избегать огромного использования памяти, если число пользователей будет больше
Basav
2
memory_limit = -1; установить в php.ini
2
@YumYumYum Это удаляет memory_limit, который вы хотите, только если вы отслеживаете использование памяти другим способом. ОС убьет процесс, если в какой-то момент потребует огромное количество памяти.
Flimm
Так что, если вы запускаете сценарий, который использует много памяти, но вам нужно запустить его только один раз, вы можете просто увеличить ограничение памяти для процесса во время выполнения, а затем снова уменьшить предел памяти после однократного скрипт работает?
Chromechris
95

Выделение памяти для PHP может быть изменено постоянно или временно.

Постоянно

Вы можете навсегда изменить распределение памяти PHP двумя способами.

Если у вас есть доступ к вашему php.iniфайлу, вы можете отредактировать значение memory_limitдо вашего желаемого значения.

Если у вас нет доступа к вашему php.iniфайлу (и ваш веб-хостинг это позволяет), вы можете переопределить распределение памяти через ваш .htaccessфайл. Добавить php_value memory_limit 128M(или что-то еще, что вы хотите).

временный

Вы можете настроить распределение памяти на лету из файла PHP. У вас просто есть код ini_set('memory_limit', '128M');(или что-то еще, что вы хотите ). Вы можете удалить ограничение памяти (хотя ограничения машины или экземпляра все еще могут применяться), установив значение «-1».

Умайр Идриес
источник
2
Спасибо, я не думал проверять, установил ли кто-то значение в .htaccess, которое переопределяло php.ini, и я не мог понять, почему +1
HostMyBus,
61

Утечки памяти в скрипте PHP очень легко - особенно если вы используете абстракцию, такую ​​как ORM. Попробуйте использовать Xdebug для профилирования вашего скрипта и выяснить, куда ушла вся эта память.

troelskn
источник
1
Я пойду попробую Xdebug. Я никогда не использовал его раньше, поэтому мне придется прочитать об этом. Спасибо за ответ! Надеюсь, я скоро найду ответ на этот вопрос ...
ArcticZero,
34
Помните, что PHP использует подсчет ссылок для управления памятью. Поэтому, если у вас есть циклические ссылки или глобальные переменные, эти объекты не будут переработаны. Это обычно корень утечек памяти в PHP.
troelskn
Xdebug показывает, что библиотека CI Xmlrpc.php отвечает за утечку памяти. Случайно ли возникнут проблемы с библиотеками XML-RPC CodeIgniter, о которых мне следует знать? Я попытался отключить всю обработку на стороне сервера, и он все еще не хватает памяти, если я кормить его достаточно данных.
ArcticZero
1
Я не знаю / не использую CI, поэтому я не знаю. Но вам, вероятно, следует попытаться найти объект, который не освобождается после использования - скорее всего, из-за циклической ссылки. Это детективная работа.
troelskn
1
Это единственный ответ, который советует на самом деле решить проблему. Другие ответы заводят память, чтобы перевязать симптом и игнорировать болезнь .
Крис Бейкер
56

Добавляя 22,5 миллиона записей в массив с помощью array_push, я получал фатальные ошибки «исчерпал память», около 20 миллионов записей, используя 4Gв качестве ограничения памяти в файле php.ini. Чтобы исправить это, я добавил заявление

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

в верхней части файла. Теперь все работает нормально. Я не знаю, есть ли у PHP утечка памяти. Это не моя работа, и мне все равно. Я просто должен сделать свою работу, и это сработало.

Программа очень проста:

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

Неустранимая ошибка указывает на строку 3, пока я не увеличил предел памяти, что устранило ошибку.

JamesAD-0
источник
10
ты имеешь ввиду ini_set('memory_limit', '8192M');?
Гоголь
2
Какая роскошь было бы иметь время, чтобы пойти и оптимизировать сценарий для чего-то подобного. Или исследуйте и сравнивайте и изучайте инструменты ETL или что-то подобное. В реальном мире мы поднимаем припуск памяти, делаем вещи и идем дальше.
Мэтью Поер
45

Я продолжал получать эту ошибку, даже с memory_limitset in php.ini, и значение, считываемое правильно с phpinfo().

Изменяя это от этого:

memory_limit=4G

К этому:

memory_limit=4096M

Это исправило проблему в PHP 7.

Дэнни Беккет
источник
23

Когда вы видите вышеупомянутую ошибку - особенно, если (tried to allocate __ bytes)это низкое значение, это может быть индикатором бесконечного цикла, как функция, которая вызывает себя без выхода:

function exhaustYourBytes()
{
    return exhaustYourBytes();
}
Кристен Уэйт
источник
19

После включения этих двух строк он начал работать:

; 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

Прем Кумар Маурья
источник
19

Вы можете исправить это, выбрав memory_limitfastcgi / fpm:

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

Поменяйте память, как со 128 на 512, см. Ниже

; 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
Дерик Финн
источник
18

Корневой каталог вашего сайта:

ini_set('memory_limit', '1024M');
Гауранг П
источник
1
это сработало для меня. люблю одну линию решений. +1 для простоты
Стив C
14

Измените ограничение памяти в файле php.ini и перезапустите Apache. После перезагрузки запустите phpinfo (); функция из любого файла PHP для memory_limitподтверждения изменения.

memory_limit = -1

Ограничение памяти -1 означает, что ограничение памяти не установлено. Это сейчас на максимуме.

Хасиб Камаль
источник
13

Для пользователей Drupal этот ответ Криса Лейна:

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

работает, но мы должны поставить его сразу после открытия

<?php

тег в файле index.php в корневом каталоге вашего сайта.

sigmapi13
источник
13

В Drupal 7 вы можете изменить ограничение памяти в файле settings.php, который находится в папке sites / default. Вокруг строки 260 вы увидите это:

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

Даже если ваши настройки php.ini достаточно высоки, вы не сможете использовать более 128 МБ, если это не указано в файле Drupal settings.php.

LK7889
источник
1
В Drupal7 нет такой строки кода в settings.php
FLY
В файле settings.php для drupal 6 также нет строки
AllisonC
12

Вместо того, чтобы изменять memory_limitзначение в вашем php.iniфайле, если есть часть вашего кода, которая может использовать много памяти, вы можете удалить memory_limitдо того, как этот раздел запустится, а затем заменить его после.

$limit = ini_get('memory_limit');
ini_set('memory_limit', -1);
// ... do heavy stuff
ini_set('memory_limit', $limit);
mykeels
источник
7

PHP 5.3+ позволяет изменить ограничение памяти, поместив .user.iniфайл в public_htmlпапку. Просто создайте файл выше и введите в нем следующую строку:

memory_limit = 64M

Некоторые хосты cPanel принимают только этот метод.

Саби
источник
7

Краш страница?

Введите описание изображения здесь

(Это происходит, когда MySQL должен запрашивать большие строки. По умолчанию memory_limitустановлено значение small, что было более безопасно для оборудования.)

Вы можете проверить состояние вашей системы в памяти, прежде чем увеличивать 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

Здесь я увеличил его как в следующем, а затем сделать, service httpd restartчтобы исправить проблему страницы сбоя.

# grep memory_limit /etc/php.ini
memory_limit = 512M
Питер Мортенсен
источник
На какой номер (строку и столбец?) Нужно взглянуть после выполнения free -mкоманды, чтобы выбрать новый memory_limit?
kiradotee
7

Просто добавьте ini_set('memory_limit', '-1');строку в верхней части вашей веб-страницы.

И вы можете установить свою память в соответствии с вашими потребностями вместо -1, до 16Mи т. Д.

Панкадж Пратик Рай
источник
6
Похоже, это говорит о том же, что и многие существующие ответы. Лучше всего добавить ответ на популярный вопрос только в том случае, если новый материал предлагает что-то новое.
halfer
6

Для тех, кто ломает голову, чтобы понять, почему эта маленькая функция должна вызывать утечку памяти, иногда по маленькой ошибке, функция начинает рекурсивно вызывать себя навсегда.

Например, прокси-класс с тем же именем для функции объекта, который собирается его прокси.

class Proxy {

    private $actualObject;

    public function doSomething() {

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

Иногда вы можете забыть привезти этого маленького члена фактического Objec, и, поскольку прокси-сервер на самом деле имеет этот doSomethingметод, PHP не выдаст вам никакой ошибки, а для большого класса он может быть скрыт от глаз на пару минут, чтобы выяснить, почему он утечка памяти.

Madz
источник
И еще один совет: вы можете вставить die('here')свой код и переместить этот оператор, чтобы увидеть, где начинается рекурсия.
Toddmo
6

У меня была ошибка ниже при работе с набором данных, меньшим, чем работал ранее.

Неустранимая ошибка: допустимый объем памяти 134217728 байт исчерпан (попытка выделить 4096 байт) в C: \ workspace \ image_management.php в строке 173

Поскольку поиск ошибки привел меня сюда, я подумал, что упомяну, что это не всегда технические решения в предыдущих ответах, но что-то более простое. В моем случае это был Firefox. До того, как я запустил программу, она уже использовала 1157 МБ.

Оказывается, я смотрел 50-минутное видео немного в течение нескольких дней, и это все испортило. Это то, что эксперты исправляют, даже не задумываясь об этом, но для таких, как я, это стоит иметь в виду.

M61Vulcan
источник
У меня было похожее событие в Google Chrome сегодня. Я очень скептически отнесся к этому ответу ... однако он показал, что истощение моего байта исчезло после того, как я открыл окно инкогнито и снова запустил тот же сценарий! Исследование продолжается.
mickmackusa
2

Запуск сценария следующим образом (например, cron): php5 /pathToScript/info.phpвыдает ту же ошибку.

Правильный путь: php5 -cli /pathToScript/info.php

РАТМ
источник
2

Если вы используете виртуальный частный сервер на базе WHM, вы можете обнаружить, что у вас нет прав для прямого редактирования PHP.INI; система должна это сделать. В панели управления хостом WHM перейдите к Конфигурация службыРедактор конфигурации PHP и измените memory_limit:

Обновление memory_limit на WHM 11.48.4

Janckos
источник
2

Я нахожу это полезным при включении или требовании _dbconnection.php_и _functions.phpв файлах, которые фактически обрабатываются, а не включается в заголовок. Который входит в себя.

Так что, если ваш заголовок и нижний колонтитул включены, просто включите все ваши функциональные файлы, прежде чем заголовок включен.

Керим
источник
2

Использование yieldможет быть решением. См. Синтаксис генератора .

Вместо того, чтобы изменять PHP.iniфайл для увеличения объема памяти, иногда реализация yieldвнутреннего цикла может решить эту проблему. То, что делает yield - вместо того, чтобы выгружать все данные сразу, он читает их один за другим, экономя много памяти.

LukeDS
источник
2
PHP.ini? Не так ли php.ini?
Питер Мортенсен
1

Эта ошибка иногда вызывается ошибкой в ​​коде PHP, которая вызывает рекурсии, включающие обработку исключений и, возможно, другие операции. К сожалению, я не смог создать крошечный пример.

В этих случаях, которые случались для меня несколько раз, set_time_limit сбой, и браузер продолжает пытаться загрузить вывод PHP либо с бесконечным циклом, либо с сообщением о фатальной ошибке, которое является темой этого вопроса.

Уменьшая допустимый размер выделения путем добавления

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

в начале вашего кода вы сможете предотвратить фатальную ошибку.

Тогда вы можете остаться с программой, которая завершается, но все еще трудно отлаживать.

На данный момент вставить BreakLoop() вызовы в вашу программу, чтобы получить контроль и выяснить, какой цикл или рекурсия в вашей программе вызывает проблему.

Определение BreakLoop следующее:

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

Аргумент $ LoopSite может быть именем функции в вашем коде. На самом деле в этом нет необходимости, поскольку сообщение об ошибке, которое вы получите, будет указывать на строку, содержащую вызов BreakLoop ().

Дэвид Спектор
источник
-1

В моем случае это была небольшая проблема с тем, как была написана функция. Утечка памяти может быть вызвана назначением нового значения входной переменной функции, например:

/**
* 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';
}
Дмитрий Кравчук
источник
-6

Когда я удалил следующие строки из моего кода, все работало хорошо!

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

Эти строки были включены в каждый файл, который я запускал. При запуске файлов один за другим все работало нормально, но при запуске всех файлов вместе возникла проблема утечки памяти. Каким-то образом "include_once" не включает вещи один раз, или я делаю что-то не так ...

Омар Аль-Аззави
источник
set_include_path(get_include_path() . get_include_path().'/phpseclib'); Это добавит путь '/ phpseclib' один раз для каждого файла, который имеет строку ... так что он может добавить его много раз! Я бы предложил поместить его в файл include_onceнастроек и файл настроек.
Farfromunique