#include "postgres.h"
#include "utils/memutils.h"
Go to the source code of this file.
Data Structures | |
struct | AllocSetContext |
struct | AllocBlockData |
struct | AllocChunkData |
Defines | |
#define | ALLOC_MINBITS 3 |
#define | ALLOCSET_NUM_FREELISTS 11 |
#define | ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS)) |
#define | ALLOC_CHUNK_FRACTION 4 |
#define | ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData)) |
#define | ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData)) |
#define | AllocPointerIsValid(pointer) PointerIsValid(pointer) |
#define | AllocSetIsValid(set) PointerIsValid(set) |
#define | AllocPointerGetChunk(ptr) ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ)) |
#define | AllocChunkGetPointer(chk) ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) |
#define | LT16(n) n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n |
#define | AllocFreeInfo(_cxt, _chunk) |
#define | AllocAllocInfo(_cxt, _chunk) |
Typedefs | |
typedef struct AllocBlockData * | AllocBlock |
typedef struct AllocChunkData * | AllocChunk |
typedef void * | AllocPointer |
typedef struct AllocSetContext | AllocSetContext |
typedef AllocSetContext * | AllocSet |
typedef struct AllocBlockData | AllocBlockData |
typedef struct AllocChunkData | AllocChunkData |
Functions | |
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, int level) |
static int | AllocSetFreeIndex (Size size) |
MemoryContext | AllocSetContextCreate (MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize) |
Variables | |
static MemoryContextMethods | AllocSetMethods |
static const unsigned char | LogTable256 [256] |
#define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData)) |
Definition at line 116 of file aset.c.
Referenced by AllocSetAlloc(), AllocSetContextCreate(), AllocSetFree(), and AllocSetRealloc().
#define ALLOC_CHUNK_FRACTION 4 |
Definition at line 102 of file aset.c.
Referenced by AllocSetContextCreate().
#define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS)) |
#define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData)) |
Definition at line 117 of file aset.c.
Referenced by AllocSetAlloc(), AllocSetContextCreate(), AllocSetFree(), and AllocSetRealloc().
#define ALLOC_MINBITS 3 |
Definition at line 98 of file aset.c.
Referenced by AllocSetAlloc(), and AllocSetFreeIndex().
#define AllocAllocInfo | ( | _cxt, | ||
_chunk | ||||
) |
Definition at line 269 of file aset.c.
Referenced by AllocSetAlloc().
#define AllocChunkGetPointer | ( | chk | ) | ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ)) |
Definition at line 206 of file aset.c.
Referenced by AllocSetAlloc(), and AllocSetRealloc().
#define AllocFreeInfo | ( | _cxt, | ||
_chunk | ||||
) |
Definition at line 268 of file aset.c.
Referenced by AllocSetFree().
#define AllocPointerGetChunk | ( | ptr | ) | ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ)) |
Definition at line 204 of file aset.c.
Referenced by AllocSetFree(), AllocSetGetChunkSpace(), and AllocSetRealloc().
#define AllocPointerIsValid | ( | pointer | ) | PointerIsValid(pointer) |
#define ALLOCSET_NUM_FREELISTS 11 |
Definition at line 99 of file aset.c.
Referenced by AllocSetFreeIndex().
#define AllocSetIsValid | ( | set | ) | PointerIsValid(set) |
Definition at line 202 of file aset.c.
Referenced by AllocSetAlloc(), AllocSetDelete(), and AllocSetReset().
#define LT16 | ( | n | ) | n, n, n, n, n, n, n, n, n, n, n, n, n, n, n, n |
typedef struct AllocBlockData* AllocBlock |
typedef struct AllocBlockData AllocBlockData |
typedef struct AllocChunkData* AllocChunk |
typedef struct AllocChunkData AllocChunkData |
typedef void* AllocPointer |
typedef AllocSetContext* AllocSet |
typedef struct AllocSetContext AllocSetContext |
static void * AllocSetAlloc | ( | MemoryContext | context, | |
Size | size | |||
) | [static] |
Definition at line 562 of file aset.c.
References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, ALLOC_MINBITS, AllocAllocInfo, AllocChunkGetPointer, AllocSetFreeIndex(), AllocSetIsValid, AllocChunkData::aset, AllocBlockData::aset, Assert, AssertArg, AllocBlockData::endptr, ereport, errcode(), errdetail(), errmsg(), ERROR, AllocBlockData::freeptr, malloc, MAXALIGN, MemoryContextStats(), AllocBlockData::next, NULL, AllocChunkData::size, and TopMemoryContext.
Referenced by AllocSetRealloc().
{ AllocSet set = (AllocSet) context; AllocBlock block; AllocChunk chunk; int fidx; Size chunk_size; Size blksize; AssertArg(AllocSetIsValid(set)); /* * If requested size exceeds maximum for chunks, allocate an entire block * for this request. */ if (size > set->allocChunkLimit) { chunk_size = MAXALIGN(size); blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; block = (AllocBlock) malloc(blksize); if (block == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %lu.", (unsigned long) size))); } block->aset = set; block->freeptr = block->endptr = ((char *) block) + blksize; chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); chunk->aset = set; chunk->size = chunk_size; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ if (size < chunk_size) ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; #endif #ifdef RANDOMIZE_ALLOCATED_MEMORY /* fill the allocated space with junk */ randomize_mem((char *) AllocChunkGetPointer(chunk), size); #endif /* * Stick the new block underneath the active allocation block, so that * we don't lose the use of the space remaining therein. */ if (set->blocks != NULL) { block->next = set->blocks->next; set->blocks->next = block; } else { block->next = NULL; set->blocks = block; } AllocAllocInfo(set, chunk); return AllocChunkGetPointer(chunk); } /* * Request is small enough to be treated as a chunk. Look in the * corresponding free list to see if there is a free chunk we could reuse. * If one is found, remove it from the free list, make it again a member * of the alloc set and return its data address. */ fidx = AllocSetFreeIndex(size); chunk = set->freelist[fidx]; if (chunk != NULL) { Assert(chunk->size >= size); set->freelist[fidx] = (AllocChunk) chunk->aset; chunk->aset = (void *) set; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ if (size < chunk->size) ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; #endif #ifdef RANDOMIZE_ALLOCATED_MEMORY /* fill the allocated space with junk */ randomize_mem((char *) AllocChunkGetPointer(chunk), size); #endif AllocAllocInfo(set, chunk); return AllocChunkGetPointer(chunk); } /* * Choose the actual chunk size to allocate. */ chunk_size = (1 << ALLOC_MINBITS) << fidx; Assert(chunk_size >= size); /* * If there is enough room in the active allocation block, we will put the * chunk into that block. Else must start a new one. */ if ((block = set->blocks) != NULL) { Size availspace = block->endptr - block->freeptr; if (availspace < (chunk_size + ALLOC_CHUNKHDRSZ)) { /* * The existing active (top) block does not have enough room for * the requested allocation, but it might still have a useful * amount of space in it. Once we push it down in the block list, * we'll never try to allocate more space from it. So, before we * do that, carve up its free space into chunks that we can put on * the set's freelists. * * Because we can only get here when there's less than * ALLOC_CHUNK_LIMIT left in the block, this loop cannot iterate * more than ALLOCSET_NUM_FREELISTS-1 times. */ while (availspace >= ((1 << ALLOC_MINBITS) + ALLOC_CHUNKHDRSZ)) { Size availchunk = availspace - ALLOC_CHUNKHDRSZ; int a_fidx = AllocSetFreeIndex(availchunk); /* * In most cases, we'll get back the index of the next larger * freelist than the one we need to put this chunk on. The * exception is when availchunk is exactly a power of 2. */ if (availchunk != ((Size) 1 << (a_fidx + ALLOC_MINBITS))) { a_fidx--; Assert(a_fidx >= 0); availchunk = ((Size) 1 << (a_fidx + ALLOC_MINBITS)); } chunk = (AllocChunk) (block->freeptr); block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ); availspace -= (availchunk + ALLOC_CHUNKHDRSZ); chunk->size = availchunk; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = 0; /* mark it free */ #endif chunk->aset = (void *) set->freelist[a_fidx]; set->freelist[a_fidx] = chunk; } /* Mark that we need to create a new block */ block = NULL; } } /* * Time to create a new regular (multi-chunk) block? */ if (block == NULL) { Size required_size; /* * The first such block has size initBlockSize, and we double the * space in each succeeding block, but not more than maxBlockSize. */ blksize = set->nextBlockSize; set->nextBlockSize <<= 1; if (set->nextBlockSize > set->maxBlockSize) set->nextBlockSize = set->maxBlockSize; /* * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more * space... but try to keep it a power of 2. */ required_size = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; while (blksize < required_size) blksize <<= 1; /* Try to allocate it */ block = (AllocBlock) malloc(blksize); /* * We could be asking for pretty big blocks here, so cope if malloc * fails. But give up if there's less than a meg or so available... */ while (block == NULL && blksize > 1024 * 1024) { blksize >>= 1; if (blksize < required_size) break; block = (AllocBlock) malloc(blksize); } if (block == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %lu.", (unsigned long) size))); } block->aset = set; block->freeptr = ((char *) block) + ALLOC_BLOCKHDRSZ; block->endptr = ((char *) block) + blksize; /* * If this is the first block of the set, make it the "keeper" block. * Formerly, a keeper block could only be created during context * creation, but allowing it to happen here lets us have fast reset * cycling even for contexts created with minContextSize = 0; that way * we don't have to force space to be allocated in contexts that might * never need any space. Don't mark an oversize block as a keeper, * however. */ if (set->keeper == NULL && blksize == set->initBlockSize) set->keeper = block; block->next = set->blocks; set->blocks = block; } /* * OK, do the allocation */ chunk = (AllocChunk) (block->freeptr); block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); Assert(block->freeptr <= block->endptr); chunk->aset = (void *) set; chunk->size = chunk_size; #ifdef MEMORY_CONTEXT_CHECKING chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ if (size < chunk->size) ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; #endif #ifdef RANDOMIZE_ALLOCATED_MEMORY /* fill the allocated space with junk */ randomize_mem((char *) AllocChunkGetPointer(chunk), size); #endif AllocAllocInfo(set, chunk); return AllocChunkGetPointer(chunk); }
MemoryContext AllocSetContextCreate | ( | MemoryContext | parent, | |
const char * | name, | |||
Size | minContextSize, | |||
Size | initBlockSize, | |||
Size | maxBlockSize | |||
) |
Definition at line 353 of file aset.c.
References ALLOC_BLOCKHDRSZ, ALLOC_CHUNK_FRACTION, ALLOC_CHUNKHDRSZ, AllocSetContext::allocChunkLimit, AllocBlockData::aset, AllocSetContext::blocks, AllocBlockData::endptr, ereport, errcode(), errdetail(), errmsg(), ERROR, AllocBlockData::freeptr, AllocSetContext::initBlockSize, AllocSetContext::keeper, malloc, MAXALIGN, AllocSetContext::maxBlockSize, MemoryContextCreate(), MemoryContextStats(), AllocBlockData::next, AllocSetContext::nextBlockSize, NULL, T_AllocSetContext, and TopMemoryContext.
Referenced by _bt_preprocess_array_keys(), _SPI_make_plan_non_temp(), _SPI_save_plan(), accumArrayResult(), afterTriggerAddEvent(), afterTriggerInvokeEvents(), AtStart_Memory(), AtSubStart_Memory(), AutoVacLauncherMain(), BackgroundWriterMain(), begin_heap_rewrite(), BeginCopy(), btvacuumscan(), BuildCachedPlan(), BuildEventTriggerCache(), CheckpointerMain(), cluster(), CompleteCachedPlan(), compute_index_stats(), CopyCachedPlan(), CopyTo(), CreateCachedPlan(), CreateCacheMemoryContext(), CreateExecutorState(), CreateExprContext(), CreatePortal(), CreateStandaloneExprContext(), createTempGistContext(), createTrgmNFA(), do_analyze_rel(), do_autovacuum(), do_compile(), do_start_worker(), each_worker(), EnablePortalManager(), EventTriggerBeginCompleteQuery(), EventTriggerInvoke(), exec_parse_message(), exec_replication_command(), ExecHashTableCreate(), ExecInitAgg(), ExecInitRecursiveUnion(), ExecInitSetOp(), ExecInitSubPlan(), ExecInitUnique(), ExecInitWindowAgg(), file_acquire_sample_rows(), geqo_eval(), GetLocalBufferStorage(), gin_xlog_startup(), ginbeginscan(), ginbuild(), gininsert(), ginInsertCleanup(), gistrescan(), hash_create(), index_register(), init_MultiFuncCall(), init_sql_fcache(), initGISTstate(), inline_function(), inline_set_returning_function(), json_array_elements(), load_hba(), load_ident(), load_relcache_init_file(), load_tzoffsets(), lookup_ts_dictionary_cache(), mdinit(), MemoryContextInit(), mXactCachePut(), NIStartBuild(), pgstat_setup_memcxt(), plperl_return_next(), plperl_spi_prepare(), plpgsql_compile_inline(), PLy_push_execution_context(), PortalCreateHoldStore(), postgresAcquireSampleRowsFunc(), postgresBeginForeignModify(), postgresBeginForeignScan(), PostgresMain(), PostmasterMain(), rebuild_database_list(), ReindexDatabase(), RelationBuildRuleLock(), RelationInitIndexAccessInfo(), ResetUnloggedRelations(), RevalidateCachedQuery(), sepgsql_avc_init(), spg_xlog_startup(), spgbeginscan(), spgbuild(), spginsert(), SPI_connect(), spi_dest_startup(), storeRow(), tokenize_file(), tuplesort_begin_common(), vacuum(), and WalWriterMain().
{ 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; context->nextBlockSize = initBlockSize; /* * Compute the allocation chunk size limit for this context. It can't be * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists. * If maxBlockSize is small then requests exceeding the maxBlockSize, or * even a significant fraction of it, should be treated as large chunks * too. For the typical case of maxBlockSize a power of 2, the chunk size * limit will be at most 1/8th maxBlockSize, so that given a stream of * requests that are all the maximum chunk size we will waste at most * 1/8th of the allocated space. * * We have to have allocChunkLimit a power of two, because the requested * and actually-allocated sizes of any chunk must be on the same side of * the limit, else we get confused about whether the chunk is "big". */ context->allocChunkLimit = ALLOC_CHUNK_LIMIT; while ((Size) (context->allocChunkLimit + ALLOC_CHUNKHDRSZ) > (Size) ((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION)) context->allocChunkLimit >>= 1; /* * 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; } return (MemoryContext) context; }
static void AllocSetDelete | ( | MemoryContext | context | ) | [static] |
Definition at line 526 of file aset.c.
References AllocSetIsValid, AssertArg, free, AllocBlockData::freeptr, MemSetAligned, AllocBlockData::next, next(), and NULL.
{ AllocSet set = (AllocSet) context; AllocBlock block = set->blocks; AssertArg(AllocSetIsValid(set)); #ifdef MEMORY_CONTEXT_CHECKING /* Check for corruption and leaks before freeing */ AllocSetCheck(context); #endif /* Make it look empty, just in case... */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); set->blocks = NULL; set->keeper = NULL; while (block != NULL) { AllocBlock next = block->next; #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); #endif free(block); block = next; } }
static void AllocSetFree | ( | MemoryContext | context, | |
void * | pointer | |||
) | [static] |
Definition at line 820 of file aset.c.
References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, AllocFreeInfo, AllocPointerGetChunk, AllocSetFreeIndex(), AllocChunkData::aset, Assert, elog, ERROR, free, AllocBlockData::freeptr, AllocBlockData::next, NULL, AllocChunkData::size, and WARNING.
Referenced by AllocSetRealloc().
{ AllocSet set = (AllocSet) context; AllocChunk chunk = AllocPointerGetChunk(pointer); AllocFreeInfo(set, chunk); #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < chunk->size) if (((char *) pointer)[chunk->requested_size] != 0x7E) elog(WARNING, "detected write past chunk end in %s %p", set->header.name, chunk); #endif if (chunk->size > set->allocChunkLimit) { /* * Big chunks are certain to have been allocated as single-chunk * blocks. Find the containing block and return it to malloc(). */ AllocBlock block = set->blocks; AllocBlock prevblock = NULL; while (block != NULL) { if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) break; prevblock = block; block = block->next; } if (block == NULL) elog(ERROR, "could not find block containing chunk %p", chunk); /* let's just make sure chunk is the only one in the block */ Assert(block->freeptr == ((char *) block) + (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)); /* OK, remove block from aset's list and free it */ if (prevblock == NULL) set->blocks = block->next; else prevblock->next = block->next; #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); #endif free(block); } else { /* Normal case, put the chunk into appropriate freelist */ int fidx = AllocSetFreeIndex(chunk->size); chunk->aset = (void *) set->freelist[fidx]; #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(pointer, 0x7F, chunk->size); #endif #ifdef MEMORY_CONTEXT_CHECKING /* Reset requested_size to 0 in chunks that are on freelist */ chunk->requested_size = 0; #endif set->freelist[fidx] = chunk; } }
static int AllocSetFreeIndex | ( | Size | size | ) | [inline, static] |
Definition at line 281 of file aset.c.
References ALLOC_MINBITS, ALLOCSET_NUM_FREELISTS, Assert, and LogTable256.
Referenced by AllocSetAlloc(), and AllocSetFree().
{ int idx; unsigned int t, tsize; if (size > (1 << ALLOC_MINBITS)) { tsize = (size - 1) >> ALLOC_MINBITS; /* * At this point we need to obtain log2(tsize)+1, ie, the number of * not-all-zero bits at the right. We used to do this with a * shift-and-count loop, but this function is enough of a hotspot to * justify micro-optimization effort. The best approach seems to be * to use a lookup table. Note that this code assumes that * ALLOCSET_NUM_FREELISTS <= 17, since we only cope with two bytes of * the tsize value. */ t = tsize >> 8; idx = t ? LogTable256[t] + 8 : LogTable256[tsize]; Assert(idx < ALLOCSET_NUM_FREELISTS); } else idx = 0; return idx; }
static Size AllocSetGetChunkSpace | ( | MemoryContext | context, | |
void * | pointer | |||
) | [static] |
Definition at line 1029 of file aset.c.
References AllocPointerGetChunk, and AllocChunkData::size.
{ AllocChunk chunk = AllocPointerGetChunk(pointer); return chunk->size + ALLOC_CHUNKHDRSZ; }
static void AllocSetInit | ( | MemoryContext | context | ) | [static] |
static bool AllocSetIsEmpty | ( | MemoryContext | context | ) | [static] |
Definition at line 1041 of file aset.c.
References MemoryContextData::isReset.
{ /* * For now, we say "empty" only if the context is new or just reset. We * could examine the freelists to determine if all space has been freed, * but it's not really worth the trouble for present uses of this * functionality. */ if (context->isReset) return true; return false; }
static void * AllocSetRealloc | ( | MemoryContext | context, | |
void * | pointer, | |||
Size | size | |||
) | [static] |
Definition at line 895 of file aset.c.
References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, AllocChunkGetPointer, AllocPointerGetChunk, AllocSetAlloc(), AllocSetFree(), Assert, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, AllocBlockData::freeptr, MAXALIGN, MemoryContextStats(), AllocBlockData::next, NULL, realloc, AllocChunkData::size, TopMemoryContext, and WARNING.
{ AllocSet set = (AllocSet) context; AllocChunk chunk = AllocPointerGetChunk(pointer); Size oldsize = chunk->size; #ifdef MEMORY_CONTEXT_CHECKING /* Test for someone scribbling on unused space in chunk */ if (chunk->requested_size < oldsize) if (((char *) pointer)[chunk->requested_size] != 0x7E) elog(WARNING, "detected write past chunk end in %s %p", set->header.name, chunk); #endif /* * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the * allocated area already is >= the new size. (In particular, we always * fall out here if the requested size is a decrease.) */ if (oldsize >= size) { #ifdef MEMORY_CONTEXT_CHECKING #ifdef RANDOMIZE_ALLOCATED_MEMORY /* We can only fill the extra space if we know the prior request */ if (size > chunk->requested_size) randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size, size - chunk->requested_size); #endif chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ if (size < oldsize) ((char *) pointer)[size] = 0x7E; #endif return pointer; } if (oldsize > set->allocChunkLimit) { /* * The chunk must have been allocated as a single-chunk block. Find * the containing block and use realloc() to make it bigger with * minimum space wastage. */ AllocBlock block = set->blocks; AllocBlock prevblock = NULL; Size chksize; Size blksize; while (block != NULL) { if (chunk == (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ)) break; prevblock = block; block = block->next; } if (block == NULL) elog(ERROR, "could not find block containing chunk %p", chunk); /* let's just make sure chunk is the only one in the block */ Assert(block->freeptr == ((char *) block) + (chunk->size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ)); /* Do the realloc */ chksize = MAXALIGN(size); blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; block = (AllocBlock) realloc(block, blksize); if (block == NULL) { MemoryContextStats(TopMemoryContext); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of memory"), errdetail("Failed on request of size %lu.", (unsigned long) size))); } block->freeptr = block->endptr = ((char *) block) + blksize; /* Update pointers since block has likely been moved */ chunk = (AllocChunk) (((char *) block) + ALLOC_BLOCKHDRSZ); if (prevblock == NULL) set->blocks = block; else prevblock->next = block; chunk->size = chksize; #ifdef MEMORY_CONTEXT_CHECKING #ifdef RANDOMIZE_ALLOCATED_MEMORY /* We can only fill the extra space if we know the prior request */ randomize_mem((char *) AllocChunkGetPointer(chunk) + chunk->requested_size, size - chunk->requested_size); #endif chunk->requested_size = size; /* set mark to catch clobber of "unused" space */ if (size < chunk->size) ((char *) AllocChunkGetPointer(chunk))[size] = 0x7E; #endif return AllocChunkGetPointer(chunk); } else { /* * Small-chunk case. We just do this by brute force, ie, allocate a * new chunk and copy the data. Since we know the existing data isn't * huge, this won't involve any great memcpy expense, so it's not * worth being smarter. (At one time we tried to avoid memcpy when it * was possible to enlarge the chunk in-place, but that turns out to * misbehave unpleasantly for repeated cycles of * palloc/repalloc/pfree: the eventually freed chunks go into the * wrong freelist for the next initial palloc request, and so we leak * memory indefinitely. See pgsql-hackers archives for 2007-08-11.) */ AllocPointer newPointer; /* allocate new chunk */ newPointer = AllocSetAlloc((MemoryContext) set, size); /* transfer existing data (certain to fit) */ memcpy(newPointer, pointer, oldsize); /* free old chunk */ AllocSetFree((MemoryContext) set, pointer); return newPointer; } }
static void AllocSetReset | ( | MemoryContext | context | ) | [static] |
Definition at line 465 of file aset.c.
References AllocSetIsValid, AssertArg, free, AllocBlockData::freeptr, MemSetAligned, AllocBlockData::next, next(), and NULL.
{ AllocSet set = (AllocSet) context; AllocBlock block; AssertArg(AllocSetIsValid(set)); #ifdef MEMORY_CONTEXT_CHECKING /* Check for corruption and leaks before freeing */ AllocSetCheck(context); #endif /* Clear chunk freelists */ MemSetAligned(set->freelist, 0, sizeof(set->freelist)); block = set->blocks; /* New blocks list is either empty or just the keeper block */ set->blocks = set->keeper; while (block != NULL) { AllocBlock next = block->next; if (block == set->keeper) { /* Reset the block, but don't return it to malloc */ char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ; #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(datastart, 0x7F, block->freeptr - datastart); #endif block->freeptr = datastart; block->next = NULL; } else { /* Normal case, release the block */ #ifdef CLOBBER_FREED_MEMORY /* Wipe freed memory for debugging purposes */ memset(block, 0x7F, block->freeptr - ((char *) block)); #endif free(block); } block = next; } /* Reset block size allocation sequence, too */ set->nextBlockSize = set->initBlockSize; }
static void AllocSetStats | ( | MemoryContext | context, | |
int | level | |||
) | [static] |
Definition at line 1059 of file aset.c.
References AllocChunkData::aset, AllocBlockData::endptr, AllocBlockData::freeptr, i, AllocBlockData::next, NULL, and AllocChunkData::size.
{ AllocSet set = (AllocSet) context; long nblocks = 0; long nchunks = 0; long totalspace = 0; long freespace = 0; AllocBlock block; AllocChunk chunk; int fidx; int i; for (block = set->blocks; block != NULL; block = block->next) { nblocks++; totalspace += block->endptr - ((char *) block); freespace += block->endptr - block->freeptr; } for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++) { for (chunk = set->freelist[fidx]; chunk != NULL; chunk = (AllocChunk) chunk->aset) { nchunks++; freespace += chunk->size + ALLOC_CHUNKHDRSZ; } } for (i = 0; i < level; i++) fprintf(stderr, " "); fprintf(stderr, "%s: %lu total in %ld blocks; %lu free (%ld chunks); %lu used\n", set->header.name, totalspace, nblocks, freespace, nchunks, totalspace - freespace); }
MemoryContextMethods AllocSetMethods [static] |
{ AllocSetAlloc, AllocSetFree, AllocSetRealloc, AllocSetInit, AllocSetReset, AllocSetDelete, AllocSetGetChunkSpace, AllocSetIsEmpty, AllocSetStats }
const unsigned char LogTable256[256] [static] |