PHP, достигший предела памяти, вызывает повторный запуск функции

Итак, у меня очень странная проблема.

Во-первых, я использую PHP 5.2.6 (как часть MAMP, хотя это также происходит на сервере LAMP с 5.2.6), пишу веб-сайт с помощью Zend Framework 1.7.2.

Во-вторых, программное обеспечение, которое я пишу, выполняет довольно сложные статистические расчеты, требующие большого объема памяти. Обычно он работает нормально, если в качестве ограничения памяти установлено 128 МБ.

Итак, самое интересное.

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

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

Однако, если я увеличу лимит памяти (скажем, до 1024 МБ), этого не произойдет. Общая вызывающая функция запускается один раз и продолжает свой веселый путь.

Итак, позвольте мне нарисовать карту ниже.

function A{
   calls function B
   calls function D
}

function B{
   calls calculation function C
}

function D{
   calls calculation function C
}

при 128 МБ памяти путь функций следующий (в и обратно к вызывающему): A->B->C->B->A->D->C->D->затем он запускает A из начало функции по какой-то причине до конца (есть условный случай, который становится истинным во время этого пути выполнения, поэтому путь не повторяется)

с 1 ГБ памяти путь функций следующий: A->B->C->B->A->D->C->D->конец A, что и должно произойти.

Если я уберу второй расчет из функции D, он сработает. Если я увеличу лимит памяти, это сработает.

Вопрос: нашел ли я ошибку в управлении памятью PHP? Или, может быть, в Zend Framework? Опять таки; увеличив лимит памяти на скрипт в php.ini, я не вижу этой проблемы. Это или удалить второй экземпляр выполнения этого расчета, который использует много памяти.

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


person jedcred    schedule 21.04.2009    source источник
comment
На что настроен ваш отчет об ошибках php? Если вы не получаете предупреждений или ошибок, возможно, установлено слишком низкое значение? Кажется, ты должен что-то получить. Попробуйте включить E_ALL и E_STRICT и посмотрите, может быть, это говорит вам что-то еще.   -  person Brett Bender    schedule 22.04.2009
comment
Действительно, мы получаем нечто большее. Получаем ошибку Out-of-Memory! Как ни странно, иногда я получал ошибку OOM, но теперь я получаю ее каждый раз. Итак, теперь у нас есть интересный случай, когда фатальная ошибка PHP перезапускает функцию, вызвавшую функцию, в которой произошла ошибка. Это адекватное поведение? Не должен ли PHP умереть из-за фатальной ошибки?   -  person jedcred    schedule 22.04.2009
comment
Мне интересно: в чем причина?   -  person soulmerge    schedule 22.04.2009


Ответы (2)


Вы проверили ход выполнения программы с помощью debug_backtrace()? Я думаю, что более вероятно, что причиной этого является ваш php-код, чем движок zend. Я бы рекомендовал помещать дамп трассировки в файл в начале каждой функции и анализировать его в первую очередь:

function myFunction() {
    file_put_contents(print_r(debug_backtrace(), true) . "\n--\n", 'trace.txt');
    //...
person soulmerge    schedule 21.04.2009

Во-первых, включите все отчеты об ошибках, включая E_STRICT.

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

person Paul Dixon    schedule 21.04.2009
comment
Смотрите комментарии в вопросе. - person jedcred; 22.04.2009