この記事では、主に PHP ソース コード 31: PHP メモリ プールのヒープ層の基礎について説明します。これを必要な友人に共有します。
PHP ソース コードに関する簡単な説明 31: PHP メモリ プールのヒープ層の基本
[概要]
PHP のメモリ マネージャーは階層型です。このマネージャーには、ストレージ層、ヒープ層、emalloc/efree 層の 3 つの層があります。ストレージ層は、PHP ソースコードの読み方メモ 30: PHP メモリ プールのストレージ層で紹介されています。ストレージ層は、実際には malloc() や mmap() などの関数を通じてシステムにメモリを適用し、要求されたメモリを解放します。 () 関数。ストレージ層は通常、比較的大きなメモリ ブロックに適用されます。ここで適用される大きなメモリは、ストレージ層の構造に必要なメモリを意味するものではなく、ヒープ層がストレージ層の割り当てメソッドを呼び出すときに適用されるメモリを意味します。セグメント形式ではメモリ割り当て方法が比較的大きいため、ストレージ層の役割はメモリ割り当て方法をヒープ層に対して透過的にすることです。
ストレージ層の上にあるのが、今日学習したいヒープ層です。ヒープ層は、上の emalloc/efree 層と対話して、ストレージ層を通じて適用されるメモリの大きなブロックを分割し、オンデマンドで提供するスケジューリング層です。ヒープ層には一連のメモリ スケジューリング戦略があり、これは PHP メモリ割り当て管理全体の中核領域です。
以下の共有はすべて、ZEND_DEBUG が開かれていない状況に基づいています。
まず、ヒープ層に関係する構造を見てください:
[構造]
/* mm block type */typedef struct _zend_mm_block_info { size_t _size;/* block的大小*/ size_t _prev;/* 计算前一个块有用到*/} zend_mm_block_info; typedef struct _zend_mm_block { zend_mm_block_info info;} zend_mm_block; typedef struct _zend_mm_small_free_block {/* 双向链表 */ zend_mm_block_info info; struct _zend_mm_free_block *prev_free_block;/* 前一个块 */ struct _zend_mm_free_block *next_free_block;/* 后一个块 */} zend_mm_small_free_block;/* 小的空闲块*/ typedef struct _zend_mm_free_block {/* 双向链表 + 树结构 */ zend_mm_block_info info; struct _zend_mm_free_block *prev_free_block;/* 前一个块 */ struct _zend_mm_free_block *next_free_block;/* 后一个块 */ struct _zend_mm_free_block **parent;/* 父结点 */ struct _zend_mm_free_block *child[2];/* 两个子结点*/} zend_mm_free_block; struct _zend_mm_heap { int use_zend_alloc;/* 是否使用zend内存管理器 */ void *(*_malloc)(size_t);/* 内存分配函数*/ void (*_free)(void*);/* 内存释放函数*/ void *(*_realloc)(void*, size_t); size_t free_bitmap;/* 小块空闲内存标识 */ size_t large_free_bitmap; /* 大块空闲内存标识*/ size_t block_size;/* 一次内存分配的段大小,即ZEND_MM_SEG_SIZE指定的大小,默认为ZEND_MM_SEG_SIZE (256 * 1024)*/ size_t compact_size;/* 压缩操作边界值,为ZEND_MM_COMPACT指定大小,默认为 2 * 1024 * 1024*/ zend_mm_segment *segments_list;/* 段指针列表 */ zend_mm_storage *storage;/* 所调用的存储层 */ size_t real_size;/* 堆的真实大小 */ size_t real_peak;/* 堆真实大小的峰值 */ size_t limit;/* 堆的内存边界 */ size_t size;/* 堆大小 */ size_t peak;/* 堆大小的峰值*/ size_t reserve_size;/* 备用堆大小*/ void *reserve;/* 备用堆 */ int overflow;/* 内存溢出数*/ int internal;#if ZEND_MM_CACHE unsigned int cached;/* 已缓存大小 */ zend_mm_free_block *cache[ZEND_MM_NUM_BUCKETS];/* 缓存数组/ #endif zend_mm_free_block *free_buckets[ZEND_MM_NUM_BUCKETS*2];/* 小块空闲内存数组 */ zend_mm_free_block *large_free_buckets[ZEND_MM_NUM_BUCKETS];/* 大块空闲内存数组*/ zend_mm_free_block *rest_buckets[2];/* 剩余内存数组 */ };
ヒープ構造のメモリ操作関数では、use_zend_alloc が no の場合、malloc タイプのメモリ割り当てが使用されます。このとき、すべての操作はヒープ層でのメモリ管理を経由せず、mallocなどの関数を直接使用します。
compact_size のデフォルトのサイズは 2 * 1024 * 1024 (2M) です。変数 ZEND_MM_COMPACT が設定されている場合、ピーク メモリがこの値を超えると、ストレージのコンパクト関数が呼び出されます。この関数のみ 現在の実装は空であり、後続のバージョンで追加される可能性があります。
reserve_size は予約ヒープのサイズです。デフォルトでは ZEND_MM_RESERVE_SIZE で、そのサイズは (8*1024) です。
*reserve は予約ヒープで、そのサイズはreserve_sizeで、エラーを報告するために使用されます。メモリがオーバーフローしたとき。
[USE_ZEND_ALLOC について]
環境変数 USE_ZEND_ALLOC を使用すると、実行時に malloc または emalloc メモリ割り当てを選択できるようになります。 malloc タイプのメモリ割り当てを使用すると、外部デバッガでメモリ使用量を監視できるようになりますが、emalloc 割り当てでは Zend メモリ マネージャの抽象化が使用されるため、内部デバッグが必要になります。
[zend_startup() -> start_memory_manager() -> alloc_globals_ctor()]
static void alloc_globals_ctor(zend_alloc_globals *alloc_globals TSRMLS_DC){ char *tmp; alloc_globals->mm_heap = zend_mm_startup(); tmp = getenv("USE_ZEND_ALLOC"); if (tmp) { alloc_globals->mm_heap->use_zend_alloc = zend_atoi(tmp, 0); if (!alloc_globals->mm_heap->use_zend_alloc) {/* 如果不使用zend的内存管理器,同直接使用malloc函数*/ alloc_globals->mm_heap->_malloc = malloc; alloc_globals->mm_heap->_free = free; alloc_globals->mm_heap->_realloc = realloc; } }}
[初期化]
[zend_mm_startup()]
ストレージ層を初期化し、セグメントサイズと圧縮境界値を初期化し、zend_mm_startup_ex() を呼び出してヒープ層を初期化します。
[zend_mm_startup() -> zend_mm_startup_ex()]
[メモリ アライメント]
メモリ アライメントの計算には明らかに 2 つの目的があります。1 つは数値を減らすことです。 2 つ目は、ストレージスペースを十分に効率的に保つことです。
# define ZEND_MM_ALIGNMENT 8 #define ZEND_MM_ALIGNMENT_MASK ~(ZEND_MM_ALIGNMENT-1) #define ZEND_MM_ALIGNED_SIZE(size)(((size) + ZEND_MM_ALIGNMENT - 1) & ZEND_MM_ALIGNMENT_MASK) #define ZEND_MM_ALIGNED_HEADER_SIZEZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_block)) #define ZEND_MM_ALIGNED_FREE_HEADER_SIZEZEND_MM_ALIGNED_SIZE(sizeof(zend_mm_small_free_block))
PHP は、割り当てられたブロックのメモリ内でメモリ アライメントを使用します。必要なメモリ サイズの下 3 桁が 0 でない場合 (8 で割り切れない場合)、下位桁に 7 を加算し、~7 を実行します。 AND 演算、つまり 8 の整数倍ではないメモリ サイズの場合、メモリ サイズは 8 で割り切れるようになります。
win32 マシンでは、一部のマクロに対応する数値サイズは次のとおりです:
ZEND_MM_MIN_SIZE=8
ZEND_MM_MAX_SMALL_SIZE=272
ZEND_MM_ALI GNED_HEADER_SIZE=8
ZEND_MM_ALIGNED_FREE_HEADER_SIZE=16
ZEND_MM_MIN_ALLOC_BLOCK_SIZE=8
ZEND_MM_ALIGNED_MIN_HEADER_SIZE =16
ZEND_MM_ALIGNED_SEGMENT_SIZE=8
サイズ 9 バイトのブロックを割り当てる場合、実際に割り当てられるサイズは ZEND_MM_ALIGNED_SIZE(9 8)=24
[ブロックの位置]
割り当てられたメモリの右 2 桁は、メモリのタイプをマークするために使用されます。
そのサイズの定義は #define ZEND_MM_TYPE_MASK ZEND_MM_LONG_CONST(0×3)です。
以下に示すコードはブロックの位置決めです。
#define ZEND_MM_NEXT_BLOCK(b)ZEND_MM_BLOCK_AT(b, ZEND_MM_BLOCK_SIZE(b)) #define ZEND_MM_PREV_BLOCK(b)ZEND_MM_BLOCK_AT(b, -(int)((b)->info._prev & ~ZEND_MM_TYPE_MASK)) #define ZEND_MM_BLOCK_AT(blk, offset)((zend_mm_block *) (((char *) (blk))+(offset))) #define ZEND_MM_BLOCK_SIZE(b)((b)->info._size & ~ZEND_MM_TYPE_MASK)#define ZEND_MM_TYPE_MASKZEND_MM_LONG_CONST(0x3)
現在のブロックの次の要素は現在のブロック ヘッド位置にブロック全体の長さを加えたもの (タイプの長さを差し引いたもの)。
現在のブロックの前の要素は、現在のブロックの先頭位置から前のブロックの長さを引いたものです (型の長さを除いたもの)。
前のブロックの長さは、ブロックの初期化時に、現在のブロックのサイズとブロックの種類の OR 演算の結果に設定されます。
上記がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。
関連する推奨事項:
PHP ソース コードの簡単な説明 30: PHP メモリ プールのストレージ層
PHP ソース コード 29 の簡単な説明: インターフェイスの継承について
## PHP ソース コード 28 の簡単な説明: クラス構造と継承について
以上がPHP ソース コードの簡単な説明 31: PHP メモリ プールのヒープ層の基本の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。