突然、アプリケーションでこれまでにないほどの問題が発生しました。Apacheのエラーログを確認することにしたところ、「zend_mm_heapが破損しています」というエラーメッセージが見つかりました。これは何を意味するのでしょうか。
OS:Fedora Core 8 Apache:2.2.9 PHP:5.2.6
突然、アプリケーションでこれまでにないほどの問題が発生しました。Apacheのエラーログを確認することにしたところ、「zend_mm_heapが破損しています」というエラーメッセージが見つかりました。これは何を意味するのでしょうか。
OS:Fedora Core 8 Apache:2.2.9 PHP:5.2.6
回答:
多くの試行錯誤の末、output_buffering
php.iniファイルの値を大きくすると、このエラーは解消されることがわかりました
これは、構成オプションを変更することで必ず解決できる問題ではありません。
設定オプションを変更すると、プラスの影響が出る場合がありますが、それと同じくらい簡単に事態が悪化したり、何も起こらない場合もあります。
エラーの性質はこれです:
#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を使用)ため、実行時にプログラムが使用できるメモリ領域を指します。
ひどいコードをいじってみると、明らかに正しくないステートメントのすべてがセグメンテーション違反(致命的な終了エラー)になるわけではないことがわかります。
サンプルコードでこれらのエラーを明示的に作成しましたが、メモリ管理環境では同じ種類のエラーが非常に簡単に発生します。たとえば、一部のコードが変数(または他のシンボル)の参照カウントを正しい方法で維持していない場合、たとえば解放が早すぎると、別のコードがすでに解放されたメモリから読み取られる可能性があり、何らかの理由でアドレスが誤って格納されると、別のコードが無効なメモリに書き込まれる可能性があり、2回解放される可能性があります...
これらは、PHPでデバッグできる問題ではなく、内部開発者の注意が絶対に必要です。
行動方針は次のとおりです。
利益がないかもしれません...私は最初に言った、あなたは設定をいじることによって症状を変える方法を見つけることができるかもしれませんが、これは非常にヒットアンドミスであり、次にあなたが持っているときに助けにはなりません同じ zend_mm_heap corrupted
メッセージ、非常に多くの構成オプションしかありません。
バグを発見したときにバグレポートを作成することは非常に重要です。バグを次にヒットした人がそれを実行するとは想定できません...おそらく、実際の解決策は不思議ではありません。問題を認識している適切な人々。
USE_ZEND_ALLOC=0
環境で設定すると、これによりZend自身のメモリマネージャが無効になります。Zendのメモリマネージャは、各リクエストに独自のヒープがあり、リクエストの最後にすべてのメモリが解放され、PHPに適切なサイズのメモリのチャンクを割り当てるように最適化されていることを保証します。
リクエストの最後でメモリを解放するためにZend MMに依存する多くの拡張コードがあるため(tut、tut)、それを無効にすると、それらの最適化が無効になります。
症状を隠すこともありますが、システムヒープはZendのヒープとまったく同じ方法で破損する可能性があります。
許容度が高いか、許容度が低いように見えますが、問題の根本原因を修正することはできません。
これを無効にする機能は、内部の開発者のためにあります。Zend MMを無効にしてPHPをデプロイしないでください。
PHP 5.5でもこれと同じエラーが発生し、出力バッファリングを増やしても解決しませんでした。私もAPCを実行していなかったので、それは問題ではありませんでした。最終的にopcacheまで追跡しましたが、cliから無効にする必要がありました。これには特定の設定がありました:
opcache.enable_cli=0
いったん切り替えると、zend_mm_heapの破損エラーは解消されました。
Linuxボックスを使用している場合は、コマンドラインでこれを試してください
export USE_ZEND_ALLOC=0
/etc/apache2/envvars
ppas(apt)からapacheとphpの両方をインストールしたubuntuサーバーでこれを実行している場合は、この行を忘れずに追加してください。PHP 7.0-RC4は、ondrejのリポジトリからインストールしたときに、このエラーをスローし始めました。
set USE_ZEND_ALLOC=0
unset()
sを確認します。デストラクタ内の(または同等の)unset()
への参照が存在しないこと、$this
およびデストラクタ内unset()
のsが原因で同じオブジェクトへの参照カウントが0に低下しないことを確認してください。腐敗。
zend_mm_heapの破損エラーに関するPHPのバグレポートがあります。コメントを見る[2011-08-31 07:49 UTC] f dot ardelian at gmail dot com
再現方法の例を。
他のすべての "解決策"(変更php.ini
、少ないモジュールでソースからPHPをコンパイルするなど)は問題を隠しているだけだと感じています。
私が試すまで、私にとって以前の答えはどれもうまくいきませんでした:
opcache.fast_shutdown=0
これは今のところうまくいくようです。
PHP 5.6とPHP-FPMおよびApache proxy_fcgiを使用しています(問題がある場合)。
バグトラッカーに従って、を設定しopcache.fast_shutdown=0
ます。高速シャットダウンでは、Zendメモリマネージャを使用して混乱を解消します。これにより、これが無効になります。
ここに1つの答えはないと思いますので、私の経験を追加します。私はこれと同じエラーをランダムなhttpd segfaultsとともに見ました。これはcPanelサーバーでした。問題の症状は、Apacheがランダムに接続をリセットすることでした(Chromeでデータが受信されなかったか、Firefoxで接続がリセットされました)。これらは一見ランダムでした-ほとんどの場合、うまくいきましたが、うまくいかないこともあります。
シーンに到着したとき、出力バッファリングはオフでした。出力のバッファリングをほのめかすこのスレッドを読むことで、私はそれをオン(= 4096)にして何が起こるかを確認しました。この時点で、全員がエラーを表示し始めました。エラーが再現できるようになったので、これは良かったです。
私は通り抜け、拡張機能を無効にし始めました。その中には、eaccellerator、pdo、ioncube loader、そして疑わしいと思われるたくさんのものがありますが、どれも助けにはなりませんでした。
私はついにいたずらなPHP拡張機能を「homeloader.so」として見つけました。これはある種のcPanel-easy-installerモジュールのようです。削除後、他の問題は発生していません。
そのメモでは、これは一般的なエラーメッセージであるように思われるので、これらのすべての回答、つまり実行できる最善策として、あなたの走行距離は異なります。
上記のすべてに失敗した場合、次のようなことも試すことができます。
幸運を。
私は一週間この問題に取り組みました、これは私のために働いた、または少なくともそう思われるので
では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秒かかりました。私はこのテストを何度も実行しましたが、毎回同じでした。毎回新しいオブジェクトを作成する代わりに(ここでは長いループがあるため)、オブジェクトを再利用する必要があると思いました。これまでに数十回以上スクリプトをテストしましたが、メモリエラーは解消されました。
PHPのMongo 2.2ドライバーを使用してこのエラーが発生しました:
$collection = $db->selectCollection('post');
$collection->ensureIndex(array('someField', 'someOtherField', 'yetAnotherField'));
^^機能しない
$collection = $db->selectCollection('post');
$collection->ensureIndex(array('someField', 'someOtherField'));
$collection->ensureIndex(array('yetAnotherField'));
^^ WORKS!(?!)
foreach(selectCollection()->find()) { $arr = .. }
PHP 5.3では、たくさんの検索を行った後、これが私にとってうまくいった解決策です:
次のコードを追加して、このページのPHPガベージコレクションを無効にしました。
<? gc_disable(); ?>
問題のあるページの終わりまで、すべてのエラーが消えました。
ソース。
トレイトを使用していて、クラスの後にトレイトが読み込まれる場合(つまり、オートロードの場合)、事前にトレイトを読み込む必要があります。
https://bugs.php.net/bug.php?id=62339
注:このバグは非常にランダムです。その性質のために。
私にとって問題はpdo_mysqlの使用でした。クエリは1960年の結果を返しました。1900件のレコードを返そうとしたところ、うまくいきました。したがって、問題はpdo_mysqlと大きすぎる配列です。元のmysql拡張でクエリを書き直したところ、うまくいきました。
$link = mysql_connect('localhost', 'user', 'xxxx') or die(mysql_error());
mysql_select_db("db", $link);
Apacheは以前のエラーを報告しませんでした。
zend_mm_heap corrupted
zend_mm_heap corrupted
zend_mm_heap corrupted
[Mon Jul 30 09:23:49 2012] [notice] child pid 8662 exit signal Segmentation fault (11)
[Mon Jul 30 09:23:50 2012] [notice] child pid 8663 exit signal Segmentation fault (11)
[Mon Jul 30 09:23:54 2012] [notice] child pid 8666 exit signal Segmentation fault (11)
[Mon Jul 30 09:23:55 2012] [notice] child pid 8670 exit signal Segmentation fault (11)
また、PHP 5.2以降で実行中に「&」を使用して明示的に参照を強制する古いコードを実行すると、このエラーとSIGSEGVにも気づきました。
PHPがmemcachedにセッション情報を格納するように構成されていたため、私にとって問題はmemcachedデーモンのクラッシュでした。それは100%のCPUを食べていて、奇妙な行動をしていました。memcachedの再起動後に問題が発生しなくなりました。
いくつかを助けるかもしれないいくつかのヒント
fedora 20、php 5.5.18
public function testRead() {
$ri = new MediaItemReader(self::getMongoColl('Media'));
foreach ($ri->dataReader(10) as $data) {
// ...
}
}
public function dataReader($numOfItems) {
$cursor = $this->getStorage()->find()->limit($numOfItems);
// here is the first place where "zend_mm_heap corrupted" error occurred
// var_dump() inside foreach-loop and generator
var_dump($cursor);
foreach ($cursor as $data) {
// ...
// and this is the second place where "zend_mm_heap corrupted" error occurred
$data['Geo'] = [
// try to access [0] index that is absent in ['Geo']
'lon' => $data['Geo'][0],
'lat' => $data['Geo'][1]
];
// ...
// Generator is used !!!
yield $data;
}
}
var_dummp()の使用は実際にはエラーではなく、デバッグのためにのみ配置され、製品コードから削除されます。しかし、zend_mm_heapが発生した実際の場所は2番目です。
私はここで同じ状況にあり、何も助けにならなかった、そしてより深刻にチェックして問題を見つけた、それはバッファに出力を送信した後にtry do die(header())で構成され、コードでこれを行った人はCakePHPのリソースを忘れていましたシンプルな「return $ this-> redirect($ url)」を作成しませんでした。
井戸を再発明しようとして、これが問題でした。
これが誰かを助けることを願っています!
USE_ZEND_ALLOC=0
エラー・ログにスタックトレースを取得するには、バグを発見し/usr/sbin/httpd: corrupted double-linked list
、私はコメントアウトすることが分かったopcache.fast_shutdown=1
私のために働きました。