メモリ管理
PHP Manual

基本的なメモリ管理

C 言語でのプログラミングでは、開発者がメモリ管理を自分で行う必要があります。 PHP はウェブサーバーのモジュールとして使われることが多いので、 メモリリークを引き起こさないようにメモリ管理が特に重要となります。 さらに注意を要するのは、PHP がスレッド環境で使われる可能性があるということです。 この場合、グローバル変数が競合状態になる可能性があります。 スレッドグローバルなデータの扱いかたについての情報は、 スレッドの分離機能を扱う スレッドセーフなリソースマネージャ のドキュメントを参照ください。

これらに加え、さらに Zend Engine 独特のパターンとして注意しなければならないのは、 比較的短時間の間に zval 構造体やその他の小さなメモリブロックの確保と解放を 頻繁に繰り返すということです。PHP のメモリ管理では、 memory_limit にも注意を払わなければなりません。

これらの要件を満たすために Zend Engine は、 リクエスト単位のデータを処理するための特別なメモリマネージャを提供しています。 リクエスト単位のデータとは、単一のリクエストを処理するためにのみ必要となるデータで リクエストが終了する時点で解放されるものです。 拡張モジュールの作者は、通常は以下の表にあるルーチンを使うだけで済むことになります。 これらは利便性を考慮してマクロとして実装されていますが、 このドキュメントでは関数として扱います。

主要メモリ API 群
プロトタイプ 説明
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 を開始させます。


メモリ管理
PHP Manual