Что значит "zend_mm_heap поврежден" - PullRequest
122 голосов
/ 12 февраля 2010

Внезапно у меня возникли проблемы с моим приложением, которых у меня никогда не было раньше. Я решил проверить журнал ошибок Apache и обнаружил сообщение об ошибке «zend_mm_heap поврежден». Что это значит.

ОС: Fedora Core 8 Apache: 2.2.9 PHP: 5.2.6

Ответы [ 37 ]

51 голосов
/ 15 декабря 2010

После долгих проб и ошибок я обнаружил, что если я увеличу значение output_buffering в файле php.ini, эта ошибка исчезнет

45 голосов
/ 18 июня 2014

Я получал ту же ошибку в PHP 5.5, и увеличение буферизации вывода не помогло. У меня тоже не было APC, так что проблема не в этом. Я наконец отследил его до opcache , мне просто пришлось отключить его из кли. Для этого была определенная настройка:

opcache.enable_cli=0

После переключения ошибка zend_mm_heap исчезла.

42 голосов
/ 10 апреля 2012

Если вы используете Linux, попробуйте это в командной строке

export USE_ZEND_ALLOC=0
39 голосов
/ 01 апреля 2016

Это не проблема, которая обязательно решается путем изменения параметров конфигурации.

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

Природа ошибки такова:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void) {
    void **mem = malloc(sizeof(char)*3);
    void *ptr;

    /* read past end */
    ptr = (char*) mem[5];   

    /* write past end */
    memcpy(mem[5], "whatever", sizeof("whatever"));

    /* free invalid pointer */
    free((void*) mem[3]);

    return 0;
}

Код выше может быть скомпилирован с:

gcc -g -o corrupt corrupt.c

При выполнении кода с помощью valgrind вы можете увидеть много ошибок памяти, кульминацией которых станет ошибка сегментации:

krakjoe@fiji:/usr/src/php-src$ valgrind ./corrupt
==9749== Memcheck, a memory error detector
==9749== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==9749== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==9749== Command: ./corrupt
==9749== 
==9749== Invalid read of size 8
==9749==    at 0x4005F7: main (an.c:10)
==9749==  Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749== 
==9749== Invalid read of size 8
==9749==    at 0x400607: main (an.c:13)
==9749==  Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client"
==9749== 
==9749== Invalid write of size 2
==9749==    at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749==    by 0x40061B: main (an.c:13)
==9749==  Address 0x50 is not stack'd, malloc'd or (recently) free'd
==9749== 
==9749== 
==9749== Process terminating with default action of signal 11 (SIGSEGV): dumping core
==9749==  Access not within mapped region at address 0x50
==9749==    at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9749==    by 0x40061B: main (an.c:13)
==9749==  If you believe this happened as a result of a stack
==9749==  overflow in your program's main thread (unlikely but
==9749==  possible), you can try to increase the size of the
==9749==  main thread stack using the --main-stacksize= flag.
==9749==  The main thread stack size used in this run was 8388608.
==9749== 
==9749== HEAP SUMMARY:
==9749==     in use at exit: 3 bytes in 1 blocks
==9749==   total heap usage: 1 allocs, 0 frees, 3 bytes allocated
==9749== 
==9749== LEAK SUMMARY:
==9749==    definitely lost: 0 bytes in 0 blocks
==9749==    indirectly lost: 0 bytes in 0 blocks
==9749==      possibly lost: 0 bytes in 0 blocks
==9749==    still reachable: 3 bytes in 1 blocks
==9749==         suppressed: 0 bytes in 0 blocks
==9749== Rerun with --leak-check=full to see details of leaked memory
==9749== 
==9749== For counts of detected and suppressed errors, rerun with: -v
==9749== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0)
Segmentation fault

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

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

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

Это не те проблемы, которые можно отлаживать в PHP, они абсолютно требуют внимания разработчика внутренних компонентов.

Порядок действий должен быть:

  1. Открыть отчет об ошибке на http://bugs.php.net
    • Если у вас есть segfault, попробуйте предоставить backtrace
    • Включите столько информации о конфигурации, сколько кажется целесообразным, в частности, если вы используете opcache, включите уровень оптимизации.
    • Продолжайте проверять отчет об ошибках на наличие обновлений, может потребоваться дополнительная информация.
  2. Если у вас загружен opcache, отключите оптимизацию
    • Я не выбираю opcache, это здорово, но известно, что некоторые из его оптимизаций вызывали сбои.
    • Если это не сработает, хотя ваш код может работать медленнее, попробуйте сначала выгрузить opcache.
    • Если что-то из этого изменит или решит проблему, обновите сделанный вами отчет об ошибке.
  3. Отключить все ненужные расширения сразу.
    • Начните активировать все свои расширения по отдельности, тщательно тестируя после каждого изменения конфигурации.
    • Если вы найдете расширение проблемы, обновите свой отчет об ошибке с дополнительной информацией.
  4. Прибыль.

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

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

USE_ZEND_ALLOC

Если вы установите USE_ZEND_ALLOC=0 в среде, это отключит собственный менеджер памяти Zend; Диспетчер памяти Zend гарантирует, что каждый запрос имеет свою собственную кучу, что вся память освобождается в конце запроса, и оптимизирована для выделения кусков памяти, точно подходящего размера для PHP.

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

Он также может скрыть симптомы, но системная куча может быть повреждена точно так же, как куча Зенда.

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

Возможность его вообще отключить, для блага внутренних разработчиков; Вы должны никогда развертывать PHP с отключенным Zend MM.

22 голосов
/ 31 августа 2011

Проверка на unset() с. Убедитесь, что вы не unset() ссылаетесь на $this (или эквиваленты) в деструкторах и что unset() s в деструкторах не приводит к тому, что счетчик ссылок на один и тот же объект падает до 0. Я сделал несколько исследования и обнаружили, что именно это обычно приводит к повреждению кучи.

Существует сообщение об ошибке PHP об ошибке zend_mm_heap повреждено . См. Комментарий [2011-08-31 07:49 UTC] f dot ardelian at gmail dot com о том, как его воспроизвести.

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

6 голосов
/ 24 апреля 2012

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

6 голосов
/ 05 февраля 2016

Для меня ни один из предыдущих ответов не работал, пока я не попробовал:

opcache.fast_shutdown=0

Кажется, это работает до сих пор.

Я использую PHP 5.6 с PHP-FPM и Apache proxy_fcgi, если это имеет значение ...

5 голосов
/ 22 октября 2016

В соответствии с трекером ошибок установите opcache.fast_shutdown=0. Быстрое выключение использует менеджер памяти Zend для очистки своего беспорядка, это отключает это.

3 голосов
/ 22 августа 2013

Не думаю, что здесь есть один ответ, поэтому я добавлю свой опыт. Я видел эту же ошибку и случайные ошибки httpd. Это был сервер cPanel. Симптом, о котором идет речь, был в том, что apache случайно сбросил соединение (данные не были получены в chrome, или соединение было сброшено в firefox). Они казались случайными - большую часть времени это работало, иногда - нет.

Когда я прибыл на сцену, буферизация выходного сигнала была выключена. Читая эту ветку, которая намекала на выходную буферизацию, я включил ее (= 4096), чтобы посмотреть, что произойдет. В этот момент они все начали показывать ошибки. Хорошо, что теперь ошибка повторяется.

Я прошел и начал отключать расширения. Среди них eaccellerator, pdo, загрузчик ioncube и многое другое, что выглядело подозрением, но ни одно не помогло.

Наконец-то я нашел непослушное расширение PHP как «homeloader.so», которое выглядит как некий модуль cPanel-easy-installer. После удаления других проблем не возникало.

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

  • Сделать ошибку повторяемой (какие условия?) Каждый раз
  • Найти общий множитель
  • Выборочное отключение любых модулей, опций и т. Д. PHP (или, если вы спешите, отключите их все, чтобы увидеть, помогает ли это, а затем выборочно включите их, пока они снова не сломаются)
  • Если это не поможет, многие из этих ответов намекают на то, что это может быть выпущено кодом. Опять же, ключ должен сделать ошибку повторяемой каждый запрос , чтобы вы могли сузить ее. Если вы подозреваете, что часть кода делает это, еще раз, после того, как ошибка повторяется, просто удалите код, пока ошибка не прекратится. Как только он останавливается, вы знаете, что последний кусок кода, который вы удалили, был виновником.

В противном случае вы также можете попробовать что-то вроде:

  • Обновление или перекомпиляция PHP. Надеюсь, что любая ошибка, вызывающая вашу проблему, исправлена.
  • Переместите ваш код в другую (тестовую) среду. Если это решит проблему, что изменилось? опции php.ini? Версия PHP? и т.д ...

Удачи.

3 голосов
/ 23 апреля 2011

Я боролся с этой проблемой в течение недели. Это сработало для меня или, по крайней мере, так кажется

В php.ini внесите эти изменения

report_memleaks = Off  
report_zend_debug = 0  

Моя настройка

Linux ubuntu 2.6.32-30-generic-pae #59-Ubuntu SMP  
with PHP Version 5.3.2-1ubuntu4.7  

Это не сработало.

Итак, я попытался использовать сценарий тестирования и попытался записать, где сценарий зависал.Я обнаружил, что незадолго до ошибки был создан экземпляр объекта php, и для его выполнения требовалось более 3 секунд, тогда как в предыдущих циклах это занимало максимум 0,4 секунды.Я проводил этот тест довольно много раз, и каждый раз один и тот же.Я подумал, что вместо того, чтобы каждый раз создавать новый объект (здесь есть длинный цикл), я должен использовать объект повторно.До сих пор я тестировал скрипт более десятка раз, и ошибки памяти исчезли!

...