PostgreSQL/開発

概要

定義

マクロ

MaxAllocSize, MaxAllocHugeSize

マクロ名説明
MaxAllocSizeアロケーションの最大サイズ(1G-1)。
pallocでは,このサイズがリミットである。
(Size) 0x3fffffff
MaxAllocHugeSizeアロケーションの最大サイズ(SIZE_MAX/2)。
XXXHogeやXXX_huge系の関数ではこのリミットとなる。
(Size) -1 >> 1

このマクロ定数を直接使うシーンはあまりない。AllocHugeSizeIsValidやAllocSizeIsValidなどのチェックマクロがMemoryAllocXXXX関数で使われている。

AllocSizeIsValid, AllocHugeSizeIsValid

#define AllocSizeIsValid(size)	((Size) (size) <= MaxAllocSize)
#define AllocHugeSizeIsValid(size)	((Size) (size) <= MaxAllocHugeSize)

サイズが最大サイズ以下かチェックする。

STANDARDCHUNKHEADERSIZE

#define STANDARDCHUNKHEADERSIZE  MAXALIGN(sizeof(StandardChunkHeader))

MemoryContextResetAndDeleteChildren

#define MemoryContextResetAndDeleteChildren(ctx) MemoryContextReset(ctx)

ALLOCSET_DEFAULT_XXXX

定数説明
ALLOCSET_DEFAULT_MINSIZE0
ALLOCSET_DEFAULT_INITSIZE(8 * 1024)
ALLOCSET_DEFAULT_MAXSIZE(8 * 1024 * 1024)

アロケーション時に使用する定型サイズのためのマクロ定数。

 サンプル 

MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                              "my context",
                                              ALLOCSET_DEFAULT_MINSIZE,
                                              ALLOCSET_DEFAULT_INITSIZE,
                                              ALLOCSET_DEFAULT_MAXSIZE);

ALLOCSET_SMALL_XXXX

定数説明
ALLOCSET_SMALL_MINSIZE0
ALLOCSET_SMALL_INITSIZE(1 * 1024)
ALLOCSET_SMALL_MAXSIZE(8 * 1024)

アロケーション時に使用する定型サイズのためのマクロ定数で,比較的小さな領域の場合。

ALLOCSET_SEPARATE_THRESHOLD

#define ALLOCSET_SEPARATE_THRESHOLD  8192

ALLOC_CHUNK_LIMIT(8192)と同じでなければならない。AllocSetContextのallocChunkLimitは最大でこの値となり,このサイズを超えるアロケーション要求は別々のブロックで確保される。

 参考 

列挙型

構造体

StandardChunkHeader

番号データ型フィールド説明
1MemoryContextcontext所属するコンテキスト
2Sizesizeメモリチャンクに割り当てられたサイズ(byte)
3Sizerequested_sizeリクエストされたサイズ(byte)

関数

MemoryContextInit

extern void MemoryContextInit(void);

 参考  MemoryContextInit() - on doxygen.postgresql.org

MemoryContextReset

// 引数1:対象のメモリコンテキスト
extern void MemoryContextReset(MemoryContext context);

 参考 

MemoryContextDelete

// 引数1:対象のメモリコンテキスト
extern void MemoryContextDelete(MemoryContext context);

 参考  MemoryContextDelete() - on doxygen.postgresql.org

MemoryContextResetOnly

// 引数1:対象のメモリコンテキスト
extern void MemoryContextResetOnly(MemoryContext context);

MemoryContextResetChildren

// 引数1:対象のメモリコンテキスト
extern void MemoryContextResetChildren(MemoryContext context);

MemoryContextDeleteChildren

// 引数1:対象のメモリコンテキスト
extern void MemoryContextDeleteChildren(MemoryContext context);

 サンプル 

#define BLOCK_SIZE 1024 * 8

void reset_callback(void *arg)
{
    MemoryContext context = (MemoryContext)arg;
    elog(NOTICE, "%s will be deleted.", context->name);
}

void sample()
{
    MemoryContext oldcontext = NULL;
    MemoryContextCallback callback;
    MemoryContextCallback callback2;

    // init parent context
    MemoryContext parent_context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "parent context",
                                                  0,
                                                  BLOCK_SIZE,
                                                  BLOCK_SIZE);

    callback.func = reset_callback;
    callback.arg = parent_context;
    MemoryContextRegisterResetCallback(parent_context, &callback);

    oldcontext = MemoryContextSwitchTo(parent_context);

    // parent context from here
    StringInfo strinfo = makeStringInfo();
    appendStringInfo(strinfo, "Hello %s", "Duke");
    elog(NOTICE, "%s", strinfo->data);

    // init child context
    MemoryContext child_context = AllocSetContextCreate(parent_context,
                                                  "child context",
                                                  0,
                                                  BLOCK_SIZE,
                                                  BLOCK_SIZE);
    callback2.func = reset_callback;
    callback2.arg = child_context;
    MemoryContextRegisterResetCallback(child_context, &callback2);

    MemoryContextSwitchTo(child_context);

    // child context from here
    StringInfo strinfo2 = makeStringInfo();
    appendStringInfo(strinfo2, "Hello %s", "Togo");
    elog(NOTICE, "%s", strinfo2->data);


    // switch back to original context.
    MemoryContextSwitchTo(oldcontext);

    // delete parent_context and child_context
    //
    //  CurrentMemoryContext
    //    |- parent_context     <--- delete
    //        |- child_context  <--- delete
    MemoryContextDeleteChildren(CurrentMemoryContext);
}

MemoryContextSetParent

// 引数1:対象のメモリコンテキスト
// 引数2:新しい親となるメモリコンテキスト
extern void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent);

/* Change a context to belong a new parent.

== Before ==
new_parent             current_parent
  |- context1            |- context <-- move to (o)
  |- context2            |- next_context

== After ==
new_parent             current_parent
  |- context (o)         |- next_context
  |- context1
  |- context2            
*/

 参考  MemoryContextSetParent() - on doxygen.postgresql.org

GetMemoryChunkSpace

// 引数1:メモリチャンクのポインタ
// 戻り値:メモリチャンクサイズ(ヘッダ含む)
extern Size GetMemoryChunkSpace(void *pointer);

 サンプル 

void test_chunk_space()
{
    elog(NOTICE, "STANDARDCHUNKHEADERSIZE = %d", STANDARDCHUNKHEADERSIZE);

    char *chunk1 = palloc(128); // 128 + STANDARDCHUNKHEADERSIZE
    elog(NOTICE, "%d", GetMemoryChunkSpace(chunk1)); // NOTICE:  152

    char *chunk2 = palloc(256-32); // 256 + STANDARDCHUNKHEADERSIZE
    elog(NOTICE, "%d", GetMemoryChunkSpace(chunk2)); // NOTICE:  280
}

GetMemoryChunkContext

// 引数1:メモリチャンクのポインタ
// 戻り値:メモリチャンクが属するメモリコンテキスト
extern MemoryContext GetMemoryChunkContext(void *pointer);

 サンプル 

void test_chunk()
{
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                         "my context",
                                                         0,
                                                         1024 * 8,
                                                         1024 * 8);

    MemoryContext oldcontext = MemoryContextSwitchTo(context);
    StringInfo strinfo = makeStringInfo();
    elog(NOTICE, "%s", GetMemoryChunkContext(strinfo)->name); // NOTICE:  my contex

    MemoryContextSwitchTo(oldcontext);
    MemoryContextDelete(context);
}

MemoryContextGetParent

// 引数1:対象のメモリコンテキスト
// 戻り値:contextの親のメモリコンテスト
extern MemoryContext MemoryContextGetParent(MemoryContext context);

MemoryContextIsEmpty

// 引数1:対象のメモリコンテキスト
// 戻り値:true:空,false:空でない
extern bool MemoryContextIsEmpty(MemoryContext context);

 参考 

MemoryContextStats

// 引数1:対象のメモリコンテキスト
extern void MemoryContextStats(MemoryContext context);

 サンプル 

void test_chunk()
{
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                         "my context",
                                                         0,
                                                         1024 * 8,
                                                         1024 * 8);


    MemoryContext oldcontext = MemoryContextSwitchTo(context);
    StringInfo strinfo = makeStringInfo();
    elog(NOTICE, "%s", GetMemoryChunkContext(strinfo)->name); // NOTICE:  my contex

    MemoryContextStats(context);
    // my context: 8192 total in 1 blocks; 7056 free (0 chunks); 1136 used

    MemoryContextSwitchTo(oldcontext);
    MemoryContextDelete(context);
}

 参考  MemoryContextStats() - on doxygen.postgresql.org

MemoryContextAllowInCriticalSection

// 引数1:対象のメモリコンテキスト
// 引数2:クリティカルセクションのメモリアローケーションを許可するか否か
//      true:許可する,false:許可しない
extern void MemoryContextAllowInCriticalSection(MemoryContext context, bool allow);

 参考  palloc() - on doxygen.postgresql.org

 実験 

//
// Debug, on enable assertion
//
void test_crit()
{
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "my context",
                                                  0,
                                                  1024 * 8,
                                                  1024 * 8);
    MemoryContextAllowInCriticalSection(context, true); // 許可しない,trueだと下のpallocは成功する
    // context->allowInCritSection = false; // 許可しない,trueだと下のpallocは成功する
    MemoryContext oldcontext = MemoryContextSwitchTo(context);

    START_CRIT_SECTION();
    // クリティカルセクション内でのメモリアローケーションは通常許可されない
    void *chunk = palloc(128);
    // TRAP: FailedAssertion("!(CritSectionCount == 0 || (CurrentMemoryContext)->allowInCritSection)", File: "mcxt.c", Line: 818)
    END_CRIT_SECTION();

    MemoryContextSwitchTo(oldcontext);
    MemoryContextDelete(context);
}

MemoryContextCheck

// 引数1:対象のメモリコンテキスト
extern void MemoryContextCheck(MemoryContext context);

MemoryContextContains

// 引数1:対象のメモリコンテキスト
// 引数2:メモリチャンクのポインタ
// 戻り値:true:含まれる,false:含まれない
extern bool MemoryContextContains(MemoryContext context, void *pointer);

 参考  MemoryContextContains() - on doxygen.postgresql.org

 サンプル 

void test_chunk_contains()
{
    void *chunk = palloc(128);
    elog(NOTICE, "%d", MemoryContextContains(CurrentMemoryContext, chunk)); // NOTICE:  1
    pfree(chunk);
}

MemoryContextCreate

// 引数1:ノードタグ
// 引数2:メモリコンテキストのサイズ(byte)
// 引数3:メモリコンテキスト操作で使用される関数群
// 引数4:親のメモリコンテキスト
// 引数5:メモリコンテキスト名
// 戻り値:作成されたメモリコンテキスト
extern MemoryContext MemoryContextCreate(NodeTag tag, Size size,
					MemoryContextMethods *methods,
					MemoryContext parent,
					const char *name);

 参考 

AllocSetContextCreate

// 引数1:親のメモリコンテキスト
// 引数2:メモリコンテキストの名前
// 引数3:最小のコンテキストサイズ
// 引数4:初期ブロックサイズ
// 引数5:最大ブロックサイズ
// 戻り値:作成されたメモリコンテキスト
extern MemoryContext AllocSetContextCreate(MemoryContext parent,
					  const char *name,
					  Size minContextSize,
					  Size initBlockSize,
					  Size maxBlockSize);

 サンプル 

void test_context_create()
{
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "my context",
                                                  0,
                                                  1024 * 8,
                                                  1024 * 8);
}

サンプルプログラム

#define BLOCK_SIZE 1024 * 8

void sample()
{
    MemoryContext oldcontext = NULL;
    MemoryContextCallback callback;

    // init context
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "my context",
                                                  0,
                                                  BLOCK_SIZE,
                                                  BLOCK_SIZE);

    // set reset callback
    callback.func = reset_callback;
    callback.arg = context;
    MemoryContextRegisterResetCallback(context, &callback);

    oldcontext = MemoryContextSwitchTo(context);

    // my context from here
    StringInfo strinfo = makeStringInfo();
    appendStringInfo(strinfo, "Hello %s", "Duke");
    elog(NOTICE, "%s", strinfo->data);

    // switch back to original context.
    MemoryContextSwitchTo(oldcontext);

    // delete context
    MemoryContextDelete(context);
}

メモリコンテキストのアロケーションとメモリチャンクの統計

メモリコンテキストのアロケーションとメモリチャンクの統計を出力してみる。

 実験 

#define ALLOCSET_NUM_FREELISTS	11

typedef struct AllocBlockData *AllocBlock;
typedef struct AllocChunkData *AllocChunk;

typedef struct AllocSetContext
{
    MemoryContextData header;
    AllocBlock	blocks;
    AllocChunk	freelist[ALLOCSET_NUM_FREELISTS];
    Size		initBlockSize;
    Size		maxBlockSize;
    Size		nextBlockSize;
    Size		allocChunkLimit;
    AllocBlock	keeper;
} AllocSetContext;
typedef AllocSetContext *AllocSet;

typedef struct AllocBlockData
{
    AllocSet	aset;
    AllocBlock	next;
    char	   *freeptr;
    char	   *endptr;
}	AllocBlockData;

typedef struct AllocChunkData
{
    void	   *aset;
    Size		size;
#ifdef MEMORY_CONTEXT_CHECKING
    Size		requested_size;
#endif
}	AllocChunkData;


// メモリコンテキストの使用状況を出力する
void show_context_stats(MemoryContext context)
{
    MemoryContextStats(context);

    MemoryContext tmp_context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "temporary context",
                                                  0,
                                                  8192,
                                                  8192);
    MemoryContext oldcontext = MemoryContextSwitchTo(tmp_context);

    // show stats
    // ----------------
    AllocSet set = (AllocSet)context;
    AllocChunk chunk;
    elog(INFO, "%s", "***** freelist *****");
    StringInfo str = makeStringInfo();
    for (int i = 0; i < ALLOCSET_NUM_FREELISTS; i++) {
        chunk = set->freelist[i];
        if (chunk != NULL) {
            appendStringInfo(str, "chunk[%d] (size = %ld byte, requested_size = %ld byte, ", i, chunk->size, chunk->requested_size);
        } else {
            appendStringInfo(str, "chunk[%d] (), ", i);
        }
        elog(INFO, "%s", str->data);
        resetStringInfo(str);
    }
    // -----------------

    MemoryContextSwitchTo(oldcontext);
    MemoryContextDelete(tmp_context);
}

void test_context_limit()
{
    MemoryContext context = AllocSetContextCreate(CurrentMemoryContext,
                                                  "my context",
                                                  0,
                                                  8192,
                                                  8192);
    MemoryContext oldcontext = MemoryContextSwitchTo(context);
    elog(NOTICE, "allocChunkLimit = %ld", ((AllocSet)context)->allocChunkLimit);
    for (int i = 0; i < 10; i++) {
        palloc(1024);
        show_context_stats(context);
    }
    elog(NOTICE, "%s", "alloc bigger chunk");
    palloc(2048); // allocChunkLimit = 1024 < 2048
    show_context_stats(context);
    MemoryContextSwitchTo(oldcontext);
}

以下が出力結果である。freelistは,ブロックの残りのチャンクスペースがリクエストサイズに満たない時に2の累乗の最大となるサイズから順に確保されている。(512, 256)
また,allocChunkLimitより大きさアロケーション要求では新しくブロックが作成される。

NOTICE:  allocChunkLimit = 1024
my context: 8192 total in 1 blocks; 7112 free (0 chunks); 1080 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 6064 free (0 chunks); 2128 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 5016 free (0 chunks); 3176 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 3968 free (0 chunks); 4224 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 2920 free (0 chunks); 5272 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 1872 free (0 chunks); 6320 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 8192 total in 1 blocks; 824 free (0 chunks); 7368 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (), 
INFO:  chunk[6] (), 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 16384 total in 2 blocks; 7936 free (2 chunks); 8448 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (size = 256 byte, requested_size = 0 byte, 
INFO:  chunk[6] (size = 512 byte, requested_size = 0 byte, 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 16384 total in 2 blocks; 6888 free (2 chunks); 9496 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (size = 256 byte, requested_size = 0 byte, 
INFO:  chunk[6] (size = 512 byte, requested_size = 0 byte, 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
my context: 16384 total in 2 blocks; 5840 free (2 chunks); 10544 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (size = 256 byte, requested_size = 0 byte, 
INFO:  chunk[6] (size = 512 byte, requested_size = 0 byte, 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 
NOTICE:  alloc bigger chunk
my context: 18488 total in 3 blocks; 5840 free (2 chunks); 12648 used
INFO:  ***** freelist *****
INFO:  chunk[0] (), 
INFO:  chunk[1] (), 
INFO:  chunk[2] (), 
INFO:  chunk[3] (), 
INFO:  chunk[4] (), 
INFO:  chunk[5] (size = 256 byte, requested_size = 0 byte, 
INFO:  chunk[6] (size = 512 byte, requested_size = 0 byte, 
INFO:  chunk[7] (), 
INFO:  chunk[8] (), 
INFO:  chunk[9] (), 
INFO:  chunk[10] (), 

参考・関連

 関連 


トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP