Zend Memory Manager は、ZendMM または ZMM と略されることが多く、C レイヤーです。動的 リクエスト バインド メモリを割り当ておよび解放する機能を提供するように設計されています。
上記の文の「リクエスト バインディング」に注目してください。
ZendMM は、主に 2 つの API 呼び出し malloc()/free()
で表される、libc の動的メモリ アロケーター上の単なる従来の層ではありません。 ZendMM は、リクエストの処理時に PHP が割り当てる必要があるリクエスト バインド メモリに関するものです。
関連する学習の推奨事項: PHP プログラミングの入門から熟練度まで
PHP何も共有しないアーキテクチャ。そうですね、100% ではありません。ステップとサイクルについて詳しく説明します。
ZendMM には、libc の API をコピーすることで libc の動的アロケーターを置き換える API が付属しています。プログラマはリクエストを処理するときに libc のアロケータの代わりにこの API を使用する必要があります。 たとえば、PHP はリクエストを処理するときに、PHP ファイルを解析します。たとえば、これらは関数やクラスの宣言になります。コンパイラは、PHP ファイルのコンパイルを開始すると、検出したクラスと関数を保存するために動的メモリを割り当てます。ただし、PHP はリクエストの最後にこれらを解放します。デフォルトでは、PHP はリクエスト間でPHP は、同じプロセスで数百または数千のリクエストを処理できます。デフォルトでは、PHP は現在のリクエストが完了すると、そのリクエストに関する情報をすべて忘れます。
「忘れた」メッセージは、リクエストの処理中に割り当てられた動的バッファを解放すると解釈されます。これは、リクエストの処理中に、従来の libc 呼び出しを使用して動的メモリを割り当てることができないことを意味します。これは完全に有効ですが、バッファを解放するのを忘れる可能性があります。
多くの
情報を忘れます。 ただし、非常にまれな情報の中には、複数のリクエストにわたって保持する必要があるものもあります。しかし、これは一般的ではありません。リクエストによって変更されないものは何ですか? 永続
オブジェクトと呼ばれるもの。繰り返しますが、これは一般的な状況ではありません。たとえば、現在の PHP 実行可能パスはリクエスト間で変更されません。その情報は永続的に割り当てられます。つまり、従来の libc のmalloc ()
を呼び出して情報を割り当てます。 ######ほかに何か?何かの文字列。たとえば、$_SERVER PHP 配列がリクエストごとに作成されるため、"_SERVER" 文字列はリクエスト間で再利用されます。したがって、
"_SERVER"
覚えておく必要があります:PHP コアまたは拡張機能を作成する場合、動的メモリ割り当てには 2 つの方法があります:
リクエスト バインディングの決定動的割り当て。
は拡張機能の設計では非常に一般的で、基本的に動的割り当ての 95% はリクエスト バインディングです。
は拡張機能ではまれです。
zend_string *foo = zend_string_init("foo", strlen("foo"), 0);
zend_string *foo = zend_string_init("foo", strlen("foo"), 1);
リクエスト限定割り当て:
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 0);
永続的割り当て:
zend_array ar; zend_hash_init(&ar, 8, NULL, NULL, 1);
これは、すべての異なる Zend API で常に同じです。通常は最後のパラメータとして渡されます。
"0" は「ZendMM を使用してこの構造体を割り当てたいので、バインディングを要求する」を意味し、
"1"
"1"
は「ZendMM を使用してこの構造体を割り当てるため、バインディングが要求される」ことを意味します 従来の libc のmalloc() を呼び出します この構造を割り当てます。」 どうやら、これらの構造体は、構造体がどのように割り当てられたかを記憶する API を提供しており、破棄されたときに正しい解放関数が使用されるようです。したがって、次のようなコードになります:
zend_string_release(foo); zend_hash_destroy(&ar);
API は、これらの構造がリクエスト バインディングを使用して割り当てられているか、永続的に割り当てられているかを認識します。前者の場合は
efree() を使用して解放され、2 番目の場合は解放されます。最初の状況は libc の free()
です。API は Zend/zend_alloc.h にあります
API は主に C マクロであり、関数ではないため、デバッグしてその動作を理解したい場合は、原則として、ご準備ください。これらの API は libc の関数をコピーしており、多くの場合関数名に「e」が追加されています。したがって、API についての詳細はあまり多くないので、間違えないように注意してください。
基本的に、最も頻繁に使用するのは emalloc(size_t)
と efree(void *)
です。
サイズ size
の単一の nmemb
を割り当て、領域をゼロに設定する ecalloc(size_t nmemb, size_t size)
も提供されます。経験豊富な C プログラマであれば、可能な限り、ecalloc()
ではなく ecalloc()
を使用するのが最善であることを知っているはずです。メモリ領域をゼロに設定します。これは、ポインタ エラーの検出に非常に役立ちます。 emalloc()
は基本的に libc malloc()
と同じように機能することに注意してください。さまざまなプールで十分な大きさの領域を検索し、最適なスペースを提供します。したがって、ガベージ コレクションされたポインターが生成される可能性があります。 次に、
、つまり emalloc(size * nmemb offset)
ですが、オーバーフローがチェックされます。この API 呼び出しは、提供する必要がある番号が信頼できないソース (ユーザーランドなど) からのものである場合に使用する必要があります。 文字列に関しては、
および estrndup(char *, size_t len)
を使用して、文字列またはバイナリ文字列をコピーできます。 何が起こっても、ZendMM から返されたポインタは、
ではなく、ZendMM の efree()
を呼び出して解放する必要があります。
ZendMM は次の機能を提供します:永続的な割り当てに関する説明。永続的な割り当ては、リクエスト間で有効なままです。通常、これを行うには通常の libc
malloc/freeを使用しますが、ZendMM には libc アロケーターのショートカット、つまり「永続的」API がいくつかあります。 API は
"p"
という文字で始まり、ZendMM 割り当てまたは永続的割り当てのいずれかを選択できます。したがって、pemalloc(size_t, 1) は単にmalloc()
であり、pefree(void *, 1)
はfree()
,# です。 ##pestrdup(void *, 1)は
strdup()です。ただ言って。
Zend Memory Manager Debug Shield
メモリ消費管理。
memory_get_usage() に反映されることも意味します。 これは、PHP プロセスのヒープ サイズを追跡するのに役立つため、拡張機能の開発者としては良いことです。
malloc()
などの従来の libc 呼び出しを使用する必要があります。いずれにせよ、注意して自分が何をしているのかを知ってください。 ZendMM を使用する場合は、大量のメモリを割り当てる必要があり、PHP のmemory_limit を超える可能性があります。したがって、別のアロケータ (libc など) を使用しますが、拡張機能によって現在のプロセス ヒープ サイズが増加することに注意してください。
memory_get_usage() は PHP では見ることができませんが、OS の機能 (/proc/{pid}/maps など) を使用して現在のヒープを分析できます。
Note ZendMM を完全に無効にする必要がある場合は、
環境変数を使用して PHP を起動できます。こうすることで、ZendMM API (emalloc() など) へのすべての呼び出しが libc 呼び出しに誘導され、ZendMM が無効になります。これは、メモリをデバッグする場合に特に便利です。閉じると、すべてのライブ ポインターが参照され、PHP のデバッグ ビルドを使用している場合は、メモリ リークについて警告が表示されます。 もう少しわかりやすく説明しましょう。現在のリクエストの最後に ZendMM がアクティブなメモリ ブロックを見つけた場合、それはこれらのメモリ ブロックがリークしていることを意味します。メモリを割り当てた人はメモリを解放しているはずなので、リクエストの終了時点で ZendMM ヒープ上にアクティブなメモリ ブロックが存在してはなりません。メモリ リーク トレース
ZendMM の主なルールを思い出してください。ZendMM はリクエストの開始時に開始され、リクエストを処理するために動的メモリが必要になったときに API を呼び出すことを期待します。現在のリクエストが終了すると、ZendMM はシャットダウンします。
如果您忘记释放块,它们将全部显示在 stderr上。此内存泄漏报告进程仅在以下情况下有效:
这是一个简单泄漏到扩展中的示例:
PHP_RINIT_FUNCTION(example) { void *foo = emalloc(128); }
在启动该扩展的情况下启动 PHP,在调试版本上会在 stderr 上生成:
[Fri Jun 9 16:04:59 2017] Script: '/tmp/foobar.php' /path/to/extension/file.c(123) : Freeing 0x00007fffeee65000 (128 bytes), script=/tmp/foobar.php === Total 1 memory leaks detected ===
当 Zend 内存管理器关闭时,在每个已处理请求的末尾,将生成这些行。
但是要当心:
你必须记住的是 ZendMM 泄漏跟踪是一个不错的奖励工具,但它不能代替真正的 C 内存调试器。
这是使用 ZendMM 时最常见的错误,以及你应该怎么做。
获取有关 PHP 生命周期的信息,以了解在扩展中何时处理请求,何时不处理。如果在请求范围之外使用 ZendMM(例如在MINIT()中),在处理第一个请求之前,ZendMM 会静默清除分配,并且可能会使用after-after-free:根本没有。
使用内存调试器。如果你在 ZendMM 返回的内存区域以下或过去写入内容,则将覆盖关键的 ZendMM 结构并触发崩溃。如果 ZendMM 能够为你检测到混乱,则可能会显示“zend_mm_heap损坏”的消息。堆栈追踪将显示从某些代码到某些 ZendMM 代码的崩溃。ZendMM 代码不会自行崩溃。如果你在 ZendMM 代码中间崩溃,那很可能意味着你在某个地方弄乱了指针。插入你喜欢的内存调试器,查找有罪的部分并进行修复。
如果分配一个 ZendMM 指针(即emalloc()
)并使用 libc 释放它(free()
),或相反的情况:你将崩溃。要严谨对待。另外,如果你将其不知道的任何指针传递给 ZendMM 的efree()
:将会崩溃。
以上がPHP用のZendメモリマネージャーの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。