C 言語でのプログラミングでは、開発者がメモリ管理を自分で行う必要があります。 PHP はウェブサーバのモジュールとして使われることが多いので、 メモリリークを引き起こさないようにメモリ管理が特に重要となります。 さらに注意を要するのは、PHP がスレッド環境で使われる可能性があるということです。 この場合、グローバル変数が競合状態になる可能性があります。 スレッドグローバルなデータの扱いかたについての情報は、 スレッドの分離機能を扱う スレッドセーフなリソースマネージャ のドキュメントを参照ください。
これらに加え、さらに Zend Engine 独特のパターンとして注意しなければならないのは、 比較的短時間の間に zval 構造体やその他の小さなメモリブロックの確保と解放を 頻繁に繰り返すということです。PHP のメモリ管理では、 memory_limit にも注意を払わなければなりません。
これらの要件を満たすために Zend Engine は、 リクエスト単位のデータを処理するための特別なメモリマネージャを提供しています。 リクエスト単位のデータとは、単一のリクエストを処理するためにのみ必要となるデータで リクエストが終了する時点で解放されるものです。 拡張モジュールの作者は、通常は以下の表にあるルーチンを使うだけで済むことになります。 これらは利便性を考慮してマクロとして実装されていますが、 このドキュメントでは関数として扱います。
プロトタイプ | 説明 |
---|---|
void *emalloc(size_t size) |
size バイトのメモリを確保する。 |
void *ecalloc(size_t nmemb, size_t size) |
size バイトのバッファを
nmemb 件ぶん作成し、ゼロで初期化する。
|
void *erealloc(void *ptr, size_t size) |
バッファ ptr のサイズを変更する。
emalloc を用いて size バイトのメモリを確保する。
|
void efree(void *ptr) |
ptr が指すバッファを解放する。
解放するバッファは emalloc で確保したものでなければならない。
|
void *safe_emalloc(size_t nmemb, size_t size, size_t offset)
|
size バイトのブロックを nmemb
個、そしてそれに加えて offset バイトを保持するバッファを確保する。
emalloc(nmemb * size + offset) と似ているが、
それに加えてオーバーフロー対策の特別なプロテクトが施される。
|
char *estrdup(const char *s) |
NULL 終端の文字列 s を保持できる大きさのバッファを確保し、
s をそのバッファにコピーする。
|
char *estrndup(const char *s, unsigned int length)
|
estrdup と似ているが、NULL 終端文字列の長さが事前にわかっている場合に使う。
|
注意: C 標準ライブラリの同等の関数群とは異なり、Zend Engine のメモリ管理関数はメモリの確保に失敗しても NULL を返しません。 そのかわりに、処理を放棄して現在のリクエストを終了させます。
上でも説明したように、メモリをきちんと管理してメモリリークを防ぐことは非常に大切です。
確保したメモリは、不要になったらすぐ解放するようにしましょう。
安全策として、Zend Engine では、上の API 群で確保したメモリは
リクエストの終了時にすべて解放するようになっています。
--enable-debug
オプションつきで PHP をビルドした場合は、
これは警告を発生させます。
例1 PHP のメモリリーク警告
ZEND_FUNCTION(leak) { long leakbytes = 3; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &leakbytes) == FAILURE) { return; } emalloc(leakbytes); }
上の例の出力は、 たとえば以下のようになります。
[Thu Oct 22 02:14:57 2009] Script: '-' /home/johannes/src/PHP_5_3/Zend/zend_builtin_functions.c(1377) : Freeing 0x088888D4 (3 bytes), script=- === Total 1 memory leaks detected ===
注意: PHP の変数を扱う際には、変数用のメモリを emalloc で確保して参照カウンタに注意を払う必要があります。 詳細は 変数の作成 を参照ください。
注意: このリーク検出機能が働くのは、emalloc で確保したメモリブロックに対してのみです。 より詳細な解析を行うには、valgrind や libumem といったメモリチェッカーを使うことをおすすめします。 これらによる解析時には、PHP のメモリチェッカーを無効化することもできます。 そのためには、環境変数 USE_ZEND_ALLOC=0 を設定してから PHP を開始させます。