ホームページ > php教程 > php手册 > PHP 原則のメモリ管理におけるいくつかの難しい点

PHP 原則のメモリ管理におけるいくつかの難しい点

WBOY
リリース: 2016-06-21 08:53:02
オリジナル
990 人が閲覧しました

PHP のメモリ管理は 2 つの部分に分かれています。最初の部分は、参照カウント、コピーオンライト、およびその他のアプリケーション指向の管理です。今日紹介する zend_alloc は、利用可能なメモリの管理方法やメモリの割り当て方法など、PHP 独自のメモリ管理について説明します。

さらに、なぜこれを書くのかというと、PHP のメモリ管理で使用される戦略、データ構造、アルゴリズムを紹介する前に情報がないからです。しかし、私たちが通常拡張機能を開発したり PHP のバグを修正したりする場合、この部分についてはまったく知識がありません。十分な理解が必要です。PHP 開発チームの多くの友人はこれについてあまり理解していません。そのため、これについて具体的に書く必要があると思います。

基本的な概念については、コードを見れば簡単に理解できるので、詳細は説明しません。 ここでは、コードを見るとわかりにくい点を中心に紹介します。 ? はは、記事を書く前に、作業の重複を避けるために既存の情報を検索しましたが、TIPI プロジェクトのこの部分の説明を見たので、この部分は理解しにくいと思います。コードを見ても

現在、英語版の紹介文も作成中です: Zend MM

Zend Memory Manager (以下、Zend MM と呼びます) は、PHP のメモリ管理ロジックです: zend_mm_heap:

という重要なデータ構造があります。

Zend MM はメモリを小さいメモリと大きいメモリの 2 つのタイプに分け、それぞれを別々に扱います。小さいメモリの場合はこの部分が最もよく使用されるため、高いパフォーマンスを追求し、大きいメモリの場合は安定性を追求します。メモリの無駄です。

したがって、メモリが少ない場合、PHP ではキャッシュ メカニズムも導入しています。

Zend MM は、割り当てが 1 つの場所で見つかるように、可能な限りキャッシュを通じてこれを実現したいと考えています。

理解しにくい点の 1 つは、free_buckets のステートメントです:

Q: free_buckets 配列の長さが ZEND_MM_NUMBER_BUCKET なのはなぜですか?

A: これは、上の図の赤いボックスに示されているように、PHP が固定長配列を使用して ZEND_MM_NUMBER_BUCKET を格納するトリックを使用しているためです。使用されていない free_buckets 要素の唯一の有用なデータ構造は、next_free_block です。したがって、メモリを節約するために、PHP は ZEND_MM_NUMBER_BUCKET * sizeof(zend_mm_free_block) サイズのメモリを割り当てず、ZEND_MM_NUMBER_BUCKET * (sizeof(*next_free_block) + sizeof(*prev_free_block)) サイズのメモリのみを使用します。

ZEND_MM_SMALL_FREE_BUCKET マクロの定義を見てみましょう:

#define ZEND_MM_SMALL_FREE_BUCKET(ヒープ, インデックス)

(zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] +

sizeof(zend_mm_free_block*) * 2 -

sizeof(zend_mm_small_free_block))

その後、Zend MM は prev ポインターと next ポインターのみが使用されることを保証するため、メモリ読み取りエラーは発生しません。

次に、理解しにくい 2 番目の点は、PHP によるlarge_free_buckets の管理です。まず、割り当てについて説明します (この部分に関する TIPI プロジェクト チームの説明はやや曖昧です)。

静的 zend_mm_free_block *zend_mm_search_large_block(zend_mm_heap *ヒープ, size_t true_size)

large_free_buckets は、ツリーと双方向リストの組み合わせであると言えます:

large_free_buckets はマクロを使用して、特定のサイズのメモリがどのインデックスに該当するかを決定します:

#define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S)

zend_mm_high_bit は true_size の最上位ビット 1 のシリアル番号 (zend_mm_high_bit) を取得し、対応するアセンブリ命令は bsr です (ここで、TIPI プロジェクトのエラーの説明は次のとおりです)。「このハッシュ関数は、サイズの桁数を計算するために使用されます。戻り値はサイズ 1 ~ 1 の数値のバイナリ コードです。

言い換えれば、large_free_buckets の各要素は、対応するインデックスでサイズ 1 のメモリ ブロックへのポインターを保持します。これは少し複雑です。たとえば、次のようになります。

たとえば、large_free_buckets[2] は、サイズ 0b1000 ~ 0b1111 のメモリのみを保存します。別の例:large_free_buckets[6] は、サイズ 0b10000000 ~ 0b11111111 のメモリのポインタを保存します。

このようにして、メモリを再割り当てするときに、Zend MM は検索に最適な領域をすばやく見つけることができ、パフォーマンスが向上します。

ただし、各要素は同じサイズのメモリ ブロックを維持する双方向リストでもあり、左右の子 (child[0] と child[1]) はそれぞれキー値 0 と 1 を表します。このキー値は ?

を参照していますか?

たとえば、true_size が 0b11010 のメモリを PHP に適用したところ、PHP は、large_free_buckets で適切なメモリを見つけるために zend_mm_search_large_block のロジックを入力しました。

1. まず、true_size に対応するインデックスを計算します。計算方法は ZEND_MM_LARGE_BUCKET_INDEX

で説明した通りです。

2. 次に、ビットマップ構造で、large_free_buckets にすでに存在する true_size より大きい使用可能なメモリがあるかどうかを確認します。存在しない場合は、次を返します。

size_t ビットマップ = ヒープ->large_free_bitmap >> インデックス;

if (ビットマップ == 0) {

NULL を返す;

}

3. free_buckets[index] に使用可能なメモリがあるかどうかを確認します:

if (UNEXPECTED((ビットマップ & 1) != 0))

4. 存在する場合は、free_buckets[index] から開始して、最適なメモリを見つけます。手順は次のとおりです。

4.1. free_buckets[index] から開始し、free_buckets[index] の現在のメモリ サイズが true_size に等しい場合、検索は終了し、正常に戻ります。

4.2. インデックスに対応する true_size の現在の最上位ビットを確認します (true_size << (ZEND_MM_NUM_BUCKETS - Index))。free_buckets[index]->child[1] の場合は検索を続けます。 ->child[1] が存在しない場合、true_size の現在の最上位ビットが 0 の場合は、free_buckets[index]->child[0] の下で検索を続けます。 0] ] が存在しない場合は、free_buckets[index]->child[1] の下で最小メモリを検索します (この時点では、free_buckets[index]->child[1] の下のメモリが存在することが保証されているため) true_size より大きい)

4.3. 開始点を2の子に変更し、ture_sizeを1つ左に移動します。

5. 上記のロジックで適切なメモリが見つからない場合は、最小の「大きなメモリ ブロック」を検索します:

/* 最小の「大きい」空きブロックを検索 */

best_fit = p = heap->large_free_buckets[index + zend_mm_low_bit(bitmap)];

while ((p = p->child[p->child[0] != NULL])) {

if (ZEND_MM_FREE_BLOCK_SIZE(p) < ZEND_MM_FREE_BLOCK_SIZE(best_fit)) {

best_fit = p;

}

}

上記のロジック (p = p->child[p->child[0] != NULL]) に注意してください。PHP は最小のメモリを見つけようとしています。

large_free_buckets がキー ツリーであると言われるのはなぜですか? 上記のロジックから、PHP はバイナリ 01 に従ってサイズをキー化し、メモリ サイズ情報をキー ツリーに反映することで、迅速な検索を容易にすることがわかります。

さらに、rest_buckets は双方向リストであり、PHP の割り当て後に残りのメモリを保存し、残りのメモリを free_buckets に無意味に挿入することによって引き起こされるパフォーマンスの問題を回避するために使用されます (ここでは、TIPI プロジェクトのエラーの説明は次のとおりです)。 "これは要素が 2 つだけある配列です。一般的に使用される挿入操作と検索操作は最初の要素、つまり heap->rest_buckets[0] に対するものです。



関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のおすすめ
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート