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)
  • MemoryContextReset関数を呼ぶ。後方互換のためのマクロ。

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);
  • メモリコンテスト管理システムの初期化を行なう。
  • postmasterのスタートアップ時に呼ばれるので,拡張機能などでこの関数を呼ぶケースはない(すでにメモリコンテキスト管理が利用できる状態になっているため)。
  • この処理の中で,TopMemoryContextというメモリコンテキストツリーのトープレベルのメモリコンテキストとErrorContextが作成される。

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

MemoryContextReset

// 引数1:対象のメモリコンテキスト
extern void MemoryContextReset(MemoryContext context);
  • 指定されたメモリコンテキスト及び子階層に割り当てられたメモリ領域を解放する。
  • 子階層のメモリコンテキストに対しては,MemoryContextDelete()(AllocSetDelete())を再帰的に呼ぶ。
  • 対象のメモリコンテキスト(context)については,MemoryContextResetOnly()(AllocSetReset())が呼ばれる。contextの最初のブロック(存在すれば)は残され,メモリチャンクは消去される(freeptrは先頭にポイントされる)。

 参考 

MemoryContextDelete

// 引数1:対象のメモリコンテキスト
extern void MemoryContextDelete(MemoryContext context);
  • 指定されたメモリコンテキストのメモリ領域を解放する。
  • 子階層のメモリコンテキストも再帰的に処理される。

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

MemoryContextResetOnly

// 引数1:対象のメモリコンテキスト
extern void MemoryContextResetOnly(MemoryContext context);
  • 指定されたメモリコンテキストをリセットする(AllocSetReset()が呼ばれる)。
  • 子階層のメモリコンテキストに対しては何もしない。

MemoryContextResetChildren

// 引数1:対象のメモリコンテキスト
extern void MemoryContextResetChildren(MemoryContext context);
  • 指定されたメモリコンテキストの子階層のメモリコンテキストに対して,再帰的にMemoryContextResetOnly関数を呼ぶ。

MemoryContextDeleteChildren

// 引数1:対象のメモリコンテキスト
extern void MemoryContextDeleteChildren(MemoryContext context);
  • 指定されたメモリコンテキストの子階層のメモリコンテキストに対して,再帰的にMemoryContextDelete関数を呼ぶ。

 サンプル 

#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            
*/
  • 指定されたメモリコンテキスト(context)を新しいメモリコンテスト(new_parent)に付け替える。

 参考  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);
  • 対象のメモリコンテストが空であるか否かを返す。
    • firstchildを持っていない場合はtrue。
    • メモリコンテキストが新しい場合またはリセットされている場合はtrue。

 参考 

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);
  • デバッグ用の関数。
  • ブロックリスト,チャンクを操作し整合性をチェックする。
  • AllocSetCheck関数が呼ばれる。

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);
  • メモリコンテキストタイプ(tag)を指定して,メモリコンテストを作成する。通常のMemoryContextXXX関数は,T_AllocSetContextというタグが使われる。
  • 最近のバージョンでは,slab.cという新しいメモリコンテキスト実装が追加されている。

 参考 

AllocSetContextCreate

// 引数1:親のメモリコンテキスト
// 引数2:メモリコンテキストの名前
// 引数3:最小のコンテキストサイズ
// 引数4:初期ブロックサイズ
// 引数5:最大ブロックサイズ
// 戻り値:作成されたメモリコンテキスト
extern MemoryContext AllocSetContextCreate(MemoryContext parent,
					  const char *name,
					  Size minContextSize,
					  Size initBlockSize,
					  Size maxBlockSize);
  • AllocSetContextを作成する。この中でMemoryContextCreate関数が呼ばれている。

 サンプル 

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] (), 

参考・関連

 関連 

コメント


[PR]

トップ   差分 バックアップ リロード   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-02-04 (日) 13:34:36 (286d)
GO TO TOP