- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- PostgreSQL/解析/MemoryContext へ行く。
Memory Context †
- フラットなメモリ空間を構造化して管理するための機能であり、backendで用いられる。
- メモリコンテキストの初期化時、TopMemoryContextとErrorContextが作成される。この他、トップレベルのメモリコンテキストには、PostmasterContext、CacheMemoryContext、MessageContext、TopTransactionContext、CurTransactionContextがあり、TopMemoryContextは、全てのメモリコンテキストの親である。
参考 - メモリコンテキストはツリーの階層で管理され、ノードは1つの親ノードを持てる。
あるコンテキストを削除することで、そのコンテキストの子である一連のメモリコンテキストを削除したりすることができる。 - メモリコンテキストはそれぞれ独立したメモリ領域を持ち、親子の関連はメモリ使用域とは関係ない。
Memory Contextの構造と操作 †
Memory Contextを使用することでメモリ管理が容易になり、メモリリークなどのバグを減らせる。
基本的なメモリコンテキストの操作イメージは以下の図の通りである。
Memory Contextに関連する構造体 †
MemoryContextData †
typedef struct MemoryContextData { NodeTag type; /* identifies exact kind of context */ /* these two fields are placed here to minimize alignment wastage: */ bool isReset; /* T = no space alloced since last reset */ bool allowInCritSection; /* allow palloc in critical section */ const MemoryContextMethods *methods; /* virtual function table */ MemoryContext parent; /* NULL if no parent (toplevel context) */ MemoryContext firstchild; /* head of linked list of children */ MemoryContext prevchild; /* previous child of same parent */ MemoryContext nextchild; /* next child of same parent */ const char *name; /* context name (just for debugging) */ const char *ident; /* context ID if any (just for debugging) */ MemoryContextCallback *reset_cbs; /* list of reset/delete callbacks */ } MemoryContextData;
参考struct MemoryContextData - https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_12_1
- メモリコンテキストは、parent(親)、childlen(子ノード)、prevchild(同階層の前のノード)、nextchild(同階層の次のノード)へのポインタを持っている。
- メモリアロケーションの操作は、struct MemoryContextMethodsの関数群が担う。
MemoryContextMethods †
メモリコンテキストの具象実装である。
typedef struct MemoryContextMethods { void *(*alloc) (MemoryContext context, Size size); /* call this free_p in case someone #define's free() */ void (*free_p) (MemoryContext context, void *pointer); void *(*realloc) (MemoryContext context, void *pointer, Size size); void (*reset) (MemoryContext context); void (*delete_context) (MemoryContext context); Size (*get_chunk_space) (MemoryContext context, void *pointer); bool (*is_empty) (MemoryContext context); void (*stats) (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals); #ifdef MEMORY_CONTEXT_CHECKING void (*check) (MemoryContext context); #endif } MemoryContextMethods;
参考struct MemoryContextMethods - https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_12_1
例えば、デフォルトで使われるAllocSetは以下の関数で構成される。
static const MemoryContextMethods AllocSetMethods = { AllocSetAlloc, AllocSetFree, AllocSetRealloc, AllocSetReset, AllocSetDelete, AllocSetGetChunkSpace, AllocSetIsEmpty, AllocSetStats #ifdef MEMORY_CONTEXT_CHECKING ,AllocSetCheck #endif };
参考MemoryContextMethods AllocSetMethods - https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_12_1
AllocSetContext †
最も標準的なMemoryContextの実装はAllocSetである。
以下はAllocSetの構造外観を図示したものである。
(詳細は、aset.cを参照して欲しい)
参考 src/backend/utils/mmgr/aset.c - https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_12_1
- メモリ領域をブロック(block: AllocBlockData)とチャンク(chunk: AllocChunkData)という概念で管理する。
ブロックは、1つまたは複数のチャンクから構成される。チャンクは、pallocごとに実際に割り当てされるメモリ領域であり、pallocで返されるメモリ番地はチャンクのデータ部である。 - ブロックのfreeptrは、ブロック内の次に割り当てるメモリ領域の先頭を指し、endptrはブロックの末尾を指す。
- allocChunkLimitを超えるサイズ要求があった場合は、新規にブロックを作成し丸々割り当てる。新たに作成したブロックは、AllocSetのブロックリストの先頭ブロックの次に挿入される。
- メモリ割り当てで要求されたサイズにマッチしない且つブロック内に空きスペースが存在する場合、空きスペースはAllocSetのfreelistの管理下に置かれる。freelistは配列であり、0番目のインデックスの8bytes領域から2のべき乗でサイズアップし、10番目は8192bytesの領域を持つ。メモリ割り当て要求があった際、本サイズを満たすチャンクがfreelistに存在する場合は、そのチャンクが使用される。
- ブロック内に要求されたサイズの空き領域が存在しない、freelistにも適切なサイズのチャンクが存在しない場合、新規にブロックが作成される。ブロックは、initBlockSizeの2のべき乗でサイズアップする。例えば、AllocSetの初期化時のブロック割り当てが1024bytesであった場合、次のブロック割り当てでは1024bytes、以降2048bytes、...とサイズアップする。ただし、サイズアップではmaxBlockSizeを超えることはない。
MemoryContextの情報 †
MemoryContextStats関数を呼ぶことで、メモリコンテキストの内部情報を知ることができる。
- MemoryContextStats() - https://git.postgresql.org/gitweb/?p=postgresql.git;a=shortlog;h=refs/tags/REL_12_1
AllocSetStats †
AllocSetの場合は、MemoryContextStats関数の呼び出しでAllocSetStats関数が呼ばれる。
出力される情報は以下の通りである。
typedef struct AllocSetContext { MemoryContextData header; /* Standard memory-context fields */ /* Info about storage allocated in this context: */ AllocBlock blocks; /* head of list of blocks in this set */ AllocChunk freelist[ALLOCSET_NUM_FREELISTS]; /* free chunk lists */ /* Allocation parameters for this context: */ Size initBlockSize; /* initial block size */ Size maxBlockSize; /* maximum block size */ Size nextBlockSize; /* next block size to allocate */ Size allocChunkLimit; /* effective chunk size limit */ AllocBlock keeper; /* keep this block over resets */ /* freelist this context could be put in, or -1 if not a candidate: */ int freeListIndex; /* index in context_freelists[], or -1 */ } AllocSetContext;
項目 | 変数 | 説明 |
---|---|---|
total | totalspace | ブロックサイズの合計(AllocSetContextのサイズも含む)(bytes) |
blocks | nblocks | ブロック数(個) |
free | freespace | ブロック内の空き領域の合計(フリーリスト、チャンクヘッダサイズ含む)(bytes) |
chunks | freechunks | フリーリスト内のチャンク数(個) |
used | totalspace - freespace | 使用領域(bytes) |
使用領域は、上記の計算からわかるように、ブロックやチャンクのヘッダサイズも含んだ値である。
アライメントやfreelistのchunk割り当てなどがあるため、実際にpallocなどのアロケーション関数に指定したメモリサイズの合計となるとは限らない。
参考までにPG12.1でのサイズを以下に掲載する。
(lldb) print sizeof(AllocChunkData) (unsigned long) $0 = 24 (lldb) print sizeof(AllocBlockData) (unsigned long) $1 = 40 (lldb) print sizeof(AllocSetContext) (unsigned long) $2 = 216