📄 aset.c
字号:
/*------------------------------------------------------------------------- * * aset.c * Allocation set definitions. * * AllocSet is our standard implementation of the abstract MemoryContext * type. * * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/utils/mmgr/aset.c,v 1.64 2005/10/15 02:49:36 momjian Exp $ * * NOTE: * This is a new (Feb. 05, 1999) implementation of the allocation set * routines. AllocSet...() does not use OrderedSet...() any more. * Instead it manages allocations in a block pool by itself, combining * many small allocations in a few bigger blocks. AllocSetFree() normally * doesn't free() memory really. It just add's the free'd area to some * list for later reuse by AllocSetAlloc(). All memory blocks are free()'d * at once on AllocSetReset(), which happens when the memory context gets * destroyed. * Jan Wieck * * Performance improvement from Tom Lane, 8/99: for extremely large request * sizes, we do want to be able to give the memory back to free() as soon * as it is pfree()'d. Otherwise we risk tying up a lot of memory in * freelist entries that might never be usable. This is specially needed * when the caller is repeatedly repalloc()'ing a block bigger and bigger; * the previous instances of the block were guaranteed to be wasted until * AllocSetReset() under the old way. * * Further improvement 12/00: as the code stood, request sizes in the * midrange between "small" and "large" were handled very inefficiently, * because any sufficiently large free chunk would be used to satisfy a * request, even if it was much larger than necessary. This led to more * and more wasted space in allocated chunks over time. To fix, get rid * of the midrange behavior: we now handle only "small" power-of-2-size * chunks as chunks. Anything "large" is passed off to malloc(). Change * the number of freelists to change the small/large boundary. * * * About CLOBBER_FREED_MEMORY: * * If this symbol is defined, all freed memory is overwritten with 0x7F's. * This is useful for catching places that reference already-freed memory. * * About MEMORY_CONTEXT_CHECKING: * * Since we usually round request sizes up to the next power of 2, there * is often some unused space immediately after a requested data area. * Thus, if someone makes the common error of writing past what they've * requested, the problem is likely to go unnoticed ... until the day when * there *isn't* any wasted space, perhaps because of different memory * alignment on a new platform, or some other effect. To catch this sort * of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond * the requested space whenever the request is less than the actual chunk * size, and verifies that the byte is undamaged when the chunk is freed. * *------------------------------------------------------------------------- */#include "postgres.h"#include "utils/memutils.h"/* Define this to detail debug alloc information *//* #define HAVE_ALLOCINFO *//*-------------------- * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS), * for k = 0 .. ALLOCSET_NUM_FREELISTS-1. * * Note that all chunks in the freelists have power-of-2 sizes. This * improves recyclability: we may waste some space, but the wasted space * should stay pretty constant as requests are made and released. * * A request too large for the last freelist is handled by allocating a * dedicated block from malloc(). The block still has a block header and * chunk header, but when the chunk is freed we'll return the whole block * to malloc(), not put it on our freelists. * * CAUTION: ALLOC_MINBITS must be large enough so that * 1<<ALLOC_MINBITS is at least MAXALIGN, * or we may fail to align the smallest chunks adequately. * 8-byte alignment is enough on all currently known machines. * * With the current parameters, request sizes up to 8K are treated as chunks, * larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS * to adjust the boundary point. *-------------------- */#define ALLOC_MINBITS 3 /* smallest chunk size is 8 bytes */#define ALLOCSET_NUM_FREELISTS 11#define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))/* Size of largest chunk that we use a fixed size for *//*-------------------- * The first block allocated for an allocset has size initBlockSize. * Each time we have to allocate another block, we double the block size * (if possible, and without exceeding maxBlockSize), so as to reduce * the bookkeeping load on malloc(). * * Blocks allocated to hold oversize chunks do not follow this rule, however; * they are just however big they need to be to hold that single chunk. *-------------------- */#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))typedef struct AllocBlockData *AllocBlock; /* forward reference */typedef struct AllocChunkData *AllocChunk;/* * AllocPointer * Aligned pointer which may be a member of an allocation set. */typedef void *AllocPointer;/* * AllocSetContext is our standard implementation of MemoryContext. * * Note: isReset means there is nothing for AllocSetReset to do. This is * different from the aset being physically empty (empty blocks list) because * we may still have a keeper block. It's also different from the set being * logically empty, because we don't attempt to detect pfree'ing the last * active chunk. */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 */ bool isReset; /* T = no space alloced since last reset */ /* Allocation parameters for this context: */ Size initBlockSize; /* initial block size */ Size maxBlockSize; /* maximum block size */ AllocBlock keeper; /* if not NULL, keep this block over resets */} AllocSetContext;typedef AllocSetContext *AllocSet;/* * AllocBlock * An AllocBlock is the unit of memory that is obtained by aset.c * from malloc(). It contains one or more AllocChunks, which are * the units requested by palloc() and freed by pfree(). AllocChunks * cannot be returned to malloc() individually, instead they are put * on freelists by pfree() and re-used by the next palloc() that has * a matching request size. * * AllocBlockData is the header data for a block --- the usable space * within the block begins at the next alignment boundary. */typedef struct AllocBlockData{ AllocSet aset; /* aset that owns this block */ AllocBlock next; /* next block in aset's blocks list */ char *freeptr; /* start of free space in this block */ char *endptr; /* end of space in this block */} AllocBlockData;/* * AllocChunk * The prefix of each piece of memory in an AllocBlock * * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h. */typedef struct AllocChunkData{ /* aset is the owning aset if allocated, or the freelist link if free */ void *aset; /* size is always the size of the usable space in the chunk */ Size size;#ifdef MEMORY_CONTEXT_CHECKING /* when debugging memory usage, also store actual requested size */ /* this is zero in a free chunk */ Size requested_size;#endif} AllocChunkData;/* * AllocPointerIsValid * True iff pointer is valid allocation pointer. */#define AllocPointerIsValid(pointer) PointerIsValid(pointer)/* * AllocSetIsValid * True iff set is valid allocation set. */#define AllocSetIsValid(set) PointerIsValid(set)#define AllocPointerGetChunk(ptr) \ ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))#define AllocChunkGetPointer(chk) \ ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))/* * These functions implement the MemoryContext API for AllocSet contexts. */static void *AllocSetAlloc(MemoryContext context, Size size);static void AllocSetFree(MemoryContext context, void *pointer);static void *AllocSetRealloc(MemoryContext context, void *pointer, Size size);static void AllocSetInit(MemoryContext context);static void AllocSetReset(MemoryContext context);static void AllocSetDelete(MemoryContext context);static Size AllocSetGetChunkSpace(MemoryContext context, void *pointer);static bool AllocSetIsEmpty(MemoryContext context);static void AllocSetStats(MemoryContext context);#ifdef MEMORY_CONTEXT_CHECKINGstatic void AllocSetCheck(MemoryContext context);#endif/* * This is the virtual function table for AllocSet contexts. */static MemoryContextMethods AllocSetMethods = { AllocSetAlloc, AllocSetFree, AllocSetRealloc, AllocSetInit, AllocSetReset, AllocSetDelete, AllocSetGetChunkSpace, AllocSetIsEmpty, AllocSetStats#ifdef MEMORY_CONTEXT_CHECKING ,AllocSetCheck#endif};/* ---------- * Debug macros * ---------- */#ifdef HAVE_ALLOCINFO#define AllocFreeInfo(_cxt, _chunk) \ fprintf(stderr, "AllocFree: %s: %p, %d\n", \ (_cxt)->header.name, (_chunk), (_chunk)->size)#define AllocAllocInfo(_cxt, _chunk) \ fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \ (_cxt)->header.name, (_chunk), (_chunk)->size)#else#define AllocFreeInfo(_cxt, _chunk)#define AllocAllocInfo(_cxt, _chunk)#endif/* ---------- * AllocSetFreeIndex - * * Depending on the size of an allocation compute which freechunk * list of the alloc set it belongs to. Caller must have verified * that size <= ALLOC_CHUNK_LIMIT. * ---------- */static inline intAllocSetFreeIndex(Size size){ int idx = 0; if (size > 0) { size = (size - 1) >> ALLOC_MINBITS; while (size != 0) { idx++; size >>= 1; } Assert(idx < ALLOCSET_NUM_FREELISTS); } return idx;}/* * Public routines *//* * AllocSetContextCreate * Create a new AllocSet context. * * parent: parent context, or NULL if top-level context * name: name of context (for debugging --- string will be copied) * minContextSize: minimum context size * initBlockSize: initial allocation block size * maxBlockSize: maximum allocation block size */MemoryContextAllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize){ AllocSet context; /* Do the type-independent part of context creation */ context = (AllocSet) MemoryContextCreate(T_AllocSetContext, sizeof(AllocSetContext), &AllocSetMethods, parent, name); /* * Make sure alloc parameters are reasonable, and save them. * * We somewhat arbitrarily enforce a minimum 1K block size. */ initBlockSize = MAXALIGN(initBlockSize); if (initBlockSize < 1024) initBlockSize = 1024; maxBlockSize = MAXALIGN(maxBlockSize); if (maxBlockSize < initBlockSize) maxBlockSize = initBlockSize; context->initBlockSize = initBlockSize; context->maxBlockSize = maxBlockSize; /* * Grab always-allocated space, if requested */ if (minContextSize > ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ) { Size blksize = MAXALIGN(minContextSize); AllocBlock block; block = (AllocBlock) malloc(blksize); if (block == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed while creating memory context \"%s\".", name))); } block->aset = context; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; block->next = context->blocks; context->blocks = block; /* Mark block as not to be released at reset time */ context->keeper = block; } context->isReset = true; return (MemoryContext) context;}/* * AllocSetInit * Context-type-specific initialization routine. * * This is called by MemoryContextCreate() after setting up the * generic MemoryContext fields and before linking the new context * into the context tree. We must do whatever is needed to make the * new context minimally valid for deletion. We must *not* risk * failure --- thus, for example, allocating more memory is not cool. * (AllocSetContextCreate can allocate memory when it gets control * back, however.) */static voidAllocSetInit(MemoryContext context){ /* * Since MemoryContextCreate already zeroed the context node, we don't * have to do anything here: it's already OK. */}/* * AllocSetReset * Frees all memory which is allocated in the given set. * * Actually, this routine has some discretion about what to do. * It should mark all allocated chunks freed, but it need not necessarily * give back all the resources the set owns. Our actual implementation is * that we hang onto any "keeper" block specified for the set. In this way, * we don't thrash malloc() when a context is repeatedly reset after small * allocations, which is typical behavior for per-tuple contexts.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -