Redis ソース コード (最新バージョンではありません) のカスタム zmalloc 関数を直接見てみましょう。この関数は、malloc などの通常の関数とまったく同じ方法で使用されます。違いは、内部実装の詳細にあります。
void *zmalloc(size_t サイズ) {
// メモリを割り当てます;
void *ptr = malloc(サイズ PREFIX_SIZE);
// 割り当て失敗により例外がスローされる;
If (!ptr) zmalloc_oom_handler(size);
// システムは「malloc_size」関数を使用できますか?
#ifdef HAVE_MALLOC_SIZE
Update_zmalloc_stat_alloc(zmalloc_size(ptr));
戻り値 ptr;
#それ以外###### //割り当てられたデータの実際のサイズをデータフィールドに保存します;
*((size_t*)ptr) = サイズ;
// アライメント後のメモリ使用量を計算し、「used_memory」変数を更新します;
Update_zmalloc_stat_alloc(サイズ PREFIX_SIZE);
// データ本体の初期位置を返します;
戻り値 (char*)ptr PREFIX_SIZE;
#endif
}
実際、標準ライブラリの malloc 関数はすでに割り当てられたメモリを自動的に調整できるため、ここでの zmalloc メソッドの主な目的は、各データ ストレージに割り当てられたメモリ サイズを正確に計算することです。メモリが割り当てられるたびに、zmalloc は、割り当てられたデータ メモリ サイズに PREFIX_SIZE サイズのメモリ空間を追加します。この PREFIX_SIZE マクロは、現在のシステムの最大メモリ アドレス空間サイズ (size_t) を表します。特定のシステムのタイプによって異なります。ここでは、この PREFIX_SIZE サイズのスペースをストレージ ユニットの「データ ヘッダー」部分として参照できます。
Redis の最初のバージョンのストレージ ユニット構造
上の図に示すように、*((size_t*)ptr) = size; ステートメントを通じて、Redis は実際に割り当てられたデータ ブロック サイズを、現在割り当てられているメモリ ブロックの最初の PREFIX_SIZE バイト、つまりデータ ヘッダーに格納します。以下では、バイナリ データ エンティティは実際に「size」サイズのメモリ空間に格納されます。ここで、update_zmalloc_stat_alloc という名前の関数は、used_memory という名前のグローバル変数を内部的に維持し、毎回新しく割り当てられるメモリ サイズを累積します。この関数は最後に、現在割り当てられているメモリのデータ本体部分を指すオフセット ポインタを返します。 update_zmalloc_stat_alloc 関数の具体的な実装詳細は次のとおりです。
#define update_zmalloc_stat_alloc(__n) do {
サイズ_t _n = (__n);
// 手動メモリ補完;
If (_n&(sizeof(long)-1)) _n = sizeof(long)-(_n&(sizeof(long)-1));
atomicIncr(used_memory, __n);
} while(0)
ここで注意すべき重要な点は、行 _n = sizeof(long)-(_n&(sizeof(long)-1)); です。マクロ関数全体は、まず、今回割り当てられたメモリ サイズが sizeof(long) の整数倍であるかどうかを判断します (64 ビット マシンは 8 バイトのメモリ アライメントに対応し、32 ビット マシンは 4 バイトのメモリ アライメントに対応します)。そうでない場合は、次に、前に示したステートメントを使用して、データ セグメントの後に対応するプレースホルダー スペースを追加し、メモリ アライメント (4/8 バイト) 要件を満たすビット数を構成します。最後の atomicIncr 関数は、スレッドの安全性を確保しながら、グローバル used_memory 変数値を更新するために使用されます。
このバージョンの Redis におけるメモリ解放とメモリ割り当てのプロセスはまったく逆です。以下に示すコードは、対応する「zfree」関数の実装の詳細です。まず、この関数は、(char*)ptr-PREFIX_SIZE ステートメントを通じてデータ ブロックの実際のサイズを含むデータ フィールドの最初のアドレスをポイントし (より低いメモリ アドレスに移動します)、次に *(( size_t*)realptr) ステートメント: ブロックによって割り当てられる実メモリ サイズ (メモリ アライメント領域を除く)。最後に、update_zmalloc_stat_free 関数を使用してグローバル変数 used_memory の値を更新し、メモリ セグメントを解放します。
void zfree(void *ptr) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
size_t oldsize;
#endif
(ptr == NULL) の場合、return;
#ifdef HAVE_MALLOC_SIZE
Update_zmalloc_stat_free(zmalloc_size(ptr));
無料(ptr);
#それ以外###### realptr = (char*)ptr-PREFIX_SIZE;
Oldsize = *((size_t*)realptr);
Update_zmalloc_stat_free(古いサイズ PREFIX_SIZE);
free(realptr);
#endif
}
以下に示すように、update_zmalloc_stat_free 関数の実装の詳細を見ると、その実行プロセスが以前の update_zmalloc_stat_alloc 関数と似ていることがわかります。追加する必要があるメモリ バイトのサイズを計算し、対応するメモリ領域のサイズを used_memory 変数から減算することで、メモリ領域の使用量を正確に計算できます。
#define update_zmalloc_stat_free(__n) do { \
サイズ_t _n = (__n); \
If (_n&(sizeof(long)-1)) _n = sizeof(long)-(_n&(sizeof(long)-1)); \
atomicDecr(used_memory,__n); \
} while(0)
以上がRedis zmalloc 関数の分析例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。