From 3b47d9cd6fa5e32444f310fb61e6eab218cc5f3e Mon Sep 17 00:00:00 2001 From: lancer Date: Mon, 19 Dec 2022 18:42:03 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9EGUC=E5=8F=82=E6=95=B0disable?= =?UTF-8?q?=5Fmemory=5Fstats=E6=8E=A7=E5=88=B6STANDARD=20MEMORY=20CONTEXT?= =?UTF-8?q?=E7=9A=84=E5=88=86=E9=85=8D=EF=BC=8C=20=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E8=AF=A5=E5=8F=82=E6=95=B0=E6=97=B6=E9=80=BB=E8=BE=91=E7=AE=80?= =?UTF-8?q?=E5=8C=96=EF=BC=8C=E5=8E=BB=E9=99=A4=E4=B8=80=E4=BA=9B=E5=86=85?= =?UTF-8?q?=E5=AD=98=E8=B7=9F=E8=B8=AA=E7=BB=9F=E8=AE=A1=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/nodes/nodes.cpp | 1 + .../backend/utils/misc/guc/guc_memory.cpp | 13 + src/common/backend/utils/mmgr/Makefile | 2 +- src/common/backend/utils/mmgr/aset.cpp | 11 +- src/common/backend/utils/mmgr/mcxt.cpp | 103 +- src/common/backend/utils/mmgr/opt_aset.cpp | 954 ++++++++++++++++++ src/common/backend/utils/mmgr/opt_mcxt.cpp | 336 ++++++ src/include/gtm/utils/palloc.h | 1 + .../knl/knl_guc/knl_instance_attr_memory.h | 1 + src/include/nodes/memnodes.h | 6 +- src/include/nodes/nodes.h | 1 + src/include/utils/memutils.h | 7 + src/include/utils/palloc.h | 6 + src/test/regress/pg_regress.cpp | 2 +- 14 files changed, 1410 insertions(+), 34 deletions(-) create mode 100644 src/common/backend/utils/mmgr/opt_aset.cpp create mode 100644 src/common/backend/utils/mmgr/opt_mcxt.cpp diff --git a/src/common/backend/nodes/nodes.cpp b/src/common/backend/nodes/nodes.cpp index e7a4b62c0..20f19e191 100755 --- a/src/common/backend/nodes/nodes.cpp +++ b/src/common/backend/nodes/nodes.cpp @@ -274,6 +274,7 @@ static const TagStr g_tagStrArr[] = {{T_Invalid, "Invalid"}, {T_MergeAction, "MergeAction"}, {T_MemoryContext, "MemoryContext"}, {T_AllocSetContext, "AllocSetContext"}, + {T_OptAllocSetContext, "OptAllocSetContext"}, {T_StackAllocSetContext, "StackAllocSetContext"}, {T_SharedAllocSetContext, "SharedAllocSetContext"}, {T_MemalignAllocSetContext, "MemalignAllocSetContext"}, diff --git a/src/common/backend/utils/misc/guc/guc_memory.cpp b/src/common/backend/utils/misc/guc/guc_memory.cpp index 4bb41af7c..d9d6a0cc4 100644 --- a/src/common/backend/utils/misc/guc/guc_memory.cpp +++ b/src/common/backend/utils/misc/guc/guc_memory.cpp @@ -273,6 +273,19 @@ static void InitMemoryConfigureNamesBool() NULL, NULL}, + // variable to disable memory stats + {{"disable_memory_stats", + PGC_POSTMASTER, + NODE_ALL, + RESOURCES_MEM, + gettext_noop("disable memory stats for query execution."), + NULL}, + &g_instance.attr.attr_memory.disable_memory_stats, + false, + NULL, + NULL, + NULL}, + #ifdef MEMORY_CONTEXT_CHECKING // variable to enable memory check {{"enable_memory_context_check_debug", diff --git a/src/common/backend/utils/mmgr/Makefile b/src/common/backend/utils/mmgr/Makefile index c1b51af64..8deabac5d 100644 --- a/src/common/backend/utils/mmgr/Makefile +++ b/src/common/backend/utils/mmgr/Makefile @@ -19,6 +19,6 @@ ifneq "$(MAKECMDGOALS)" "clean" endif endif endif -OBJS = aset.o mcxt.o portalmem.o memprot.o asetstk.o asetalg.o memtrack.o AsanMemoryAllocator.o memgroup.o memtrace.o mem_snapshot.o +OBJS = aset.o mcxt.o opt_aset.o opt_mcxt.o portalmem.o memprot.o asetstk.o asetalg.o memtrack.o AsanMemoryAllocator.o memgroup.o memtrace.o mem_snapshot.o include $(top_srcdir)/src/gausskernel/common.mk diff --git a/src/common/backend/utils/mmgr/aset.cpp b/src/common/backend/utils/mmgr/aset.cpp index f33d88b18..1f797a094 100644 --- a/src/common/backend/utils/mmgr/aset.cpp +++ b/src/common/backend/utils/mmgr/aset.cpp @@ -379,9 +379,14 @@ MemoryContext AllocSetContextCreate(_in_ MemoryContext parent, _in_ const char* } switch (contextType) { #ifndef ENABLE_MEMORY_CHECK - case STANDARD_CONTEXT: - return GenericMemoryAllocator::AllocSetContextCreate( - parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, false, isSession); + case STANDARD_CONTEXT: { + if (g_instance.attr.attr_memory.disable_memory_stats) { + return opt_AllocSetContextCreate(parent, name, minContextSize, initBlockSize, maxBlockSize); + } else { + return GenericMemoryAllocator::AllocSetContextCreate( + parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, false, isSession); + } + } case SHARED_CONTEXT: return GenericMemoryAllocator::AllocSetContextCreate( parent, name, minContextSize, initBlockSize, maxBlockSize, maxSize, true, false); diff --git a/src/common/backend/utils/mmgr/mcxt.cpp b/src/common/backend/utils/mmgr/mcxt.cpp index b2e8a4985..b2ec52ea1 100644 --- a/src/common/backend/utils/mmgr/mcxt.cpp +++ b/src/common/backend/utils/mmgr/mcxt.cpp @@ -216,6 +216,10 @@ void MemoryContextReset(MemoryContext context) { AssertArg(MemoryContextIsValid(context)); + if (IsOptAllocSetContext(context)) { + return opt_MemoryContextReset(context); + } + PreventActionOnSealedContext(context); if (MemoryContextIsShared(context)) @@ -300,6 +304,11 @@ void MemoryContextDeleteInternal(MemoryContext context, bool parent_locked, /* And not CurrentMemoryContext, either */ Assert(context != CurrentMemoryContext); + if (IsOptAllocSetContext(context)) { + opt_MemoryContextDeleteInternal(context, context_list); + return; + } + MemoryContextDeleteChildren(context, context_list); #ifdef MEMORY_CONTEXT_CHECKING @@ -991,6 +1000,11 @@ void MemoryContextCheckSessionMemory(MemoryContext context, Size size, const cha void* MemoryAllocFromContext(MemoryContext context, Size size, const char* file, int line) { void* ret = NULL; + + if (IsOptAllocSetContext(context)) { + return opt_MemoryAllocFromContext(context, size); + } + if (!AllocSizeIsValid(size)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1049,6 +1063,10 @@ void* MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* { void* ret = NULL; + if (IsOptAllocSetContext(context)) { + return opt_MemoryContextAllocZeroDebug(context, size, file, line); + } + AssertArg(MemoryContextIsValid(context)); #ifdef MEMORY_CONTEXT_CHECKING PreventActionOnSealedContext(context); @@ -1099,6 +1117,10 @@ void* MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const { void* ret = NULL; + if (IsOptAllocSetContext(context)) { + return opt_MemoryContextAllocZeroAlignedDebug(context, size, file, line); + } + AssertArg(MemoryContextIsValid(context)); PreventActionOnSealedContext(context); @@ -1275,6 +1297,11 @@ void pfree(void* pointer) context = &(block->aset->header); #endif + if (IsOptAllocSetContext(context)) { + opt_pfree(pointer); + return; + } + AssertArg(MemoryContextIsValid(context)); #ifdef MEMORY_CONTEXT_CHECKING if (!IsTopMemCxt(context)) { @@ -1292,6 +1319,20 @@ void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int li MemoryContext context; void* ret = NULL; +#ifndef ENABLE_MEMORY_CHECK + /* + * OK, it's probably safe to look at the chunk header. + */ + context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; +#else + AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); + context = &(block->aset->header); +#endif + + if (IsOptAllocSetContext(context)) { + return opt_repalloc_noexcept_Debug(pointer, size, file, line); + } + if (!AllocSizeIsValid(size)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1306,16 +1347,6 @@ void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int li Assert(pointer != NULL); Assert(pointer == (void*)MAXALIGN(pointer)); -#ifndef ENABLE_MEMORY_CHECK - /* - * OK, it's probably safe to look at the chunk header. - */ - context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; -#else - AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); - context = &(block->aset->header); -#endif - AssertArg(MemoryContextIsValid(context)); /* isReset must be false already */ @@ -1346,6 +1377,20 @@ void* repallocDebug(void* pointer, Size size, const char* file, int line) MemoryContext context; void* ret = NULL; +#ifndef ENABLE_MEMORY_CHECK + /* + * OK, it's probably safe to look at the chunk header. + */ + context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; +#else + AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); + context = &(block->aset->header); +#endif + + if (IsOptAllocSetContext(context)) { + return opt_repallocDebug(pointer, size, file, line); + } + if (!AllocSizeIsValid(size)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -1359,15 +1404,7 @@ void* repallocDebug(void* pointer, Size size, const char* file, int line) */ Assert(pointer != NULL); Assert(pointer == (void*)MAXALIGN(pointer)); -#ifndef ENABLE_MEMORY_CHECK - /* - * OK, it's probably safe to look at the chunk header. - */ - context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; -#else - AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); - context = &(block->aset->header); -#endif + AssertArg(MemoryContextIsValid(context)); PreventActionOnSealedContext(context); /* isReset must be false already */ @@ -1455,6 +1492,10 @@ void* MemoryContextAllocHugeDebug(MemoryContext context, Size size, const char* { void* ret = NULL; + if (IsOptAllocSetContext(context)) { + return opt_MemoryAllocFromContext(context, size); + } + AssertArg(MemoryContextIsValid(context)); if (!AllocHugeSizeIsValid(size)) { @@ -1502,6 +1543,20 @@ void* repallocHugeDebug(void* pointer, Size size, const char* file, int line) MemoryContext context; void* ret = NULL; +#ifndef ENABLE_MEMORY_CHECK + /* + * OK, it's probably safe to look at the chunk header. + */ + context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; +#else + AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); + context = &(block->aset->header); +#endif + + if (IsOptAllocSetContext(context)) { + return opt_repallocDebug(pointer, size, file, line); + } + if (!AllocHugeSizeIsValid(size)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("invalid memory alloc request size %lu in %s:%d", size, file, line))); @@ -1514,15 +1569,7 @@ void* repallocHugeDebug(void* pointer, Size size, const char* file, int line) */ Assert(pointer != NULL); Assert(pointer == (void*)MAXALIGN(pointer)); -#ifndef ENABLE_MEMORY_CHECK - /* - * OK, it's probably safe to look at the chunk header. - */ - context = ((StandardChunkHeader*)((char*)pointer - STANDARDCHUNKHEADERSIZE))->context; -#else - AsanBlock block = ((AsanBlock)(((char*)(pointer)) - ASAN_BLOCKHDRSZ)); - context = &(block->aset->header); -#endif + AssertArg(MemoryContextIsValid(context)); /* isReset must be false already */ diff --git a/src/common/backend/utils/mmgr/opt_aset.cpp b/src/common/backend/utils/mmgr/opt_aset.cpp new file mode 100644 index 000000000..84b93faa6 --- /dev/null +++ b/src/common/backend/utils/mmgr/opt_aset.cpp @@ -0,0 +1,954 @@ +/* ------------------------------------------------------------------------- + * + * std_aset.cpp + * Allocation set definitions. + * + * AllocSet is our standard implementation of the abstract MemoryContext + * type. + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/utils/mmgr/aset.c + * + * 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 + +#include "postgres.h" +#include "knl/knl_variable.h" + +#include "utils/dynahash.h" +#include "utils/memutils.h" +#include "utils/mmpool.h" +#include "utils/aset.h" +#include "gssignal/gs_signal.h" +#include "gs_register/gs_malloc.h" +#include "pgstat.h" +#include "miscadmin.h" +#include "utils/memprot.h" +#include "utils/memtrack.h" +#include "storage/procarray.h" + +/* Define this to detail debug alloc information . 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< (1 << ALLOC_MINBITS)) { + idx = 31 - __builtin_clz((uint32) size - 1) - ALLOC_MINBITS + 1; + Assert(idx < ALLOCSET_NUM_FREELISTS); + } else + idx = 0; + + return idx; +} + +static void* opt_AllocSetAlloc(MemoryContext context, Size align, Size size, const char* file, int line); +static void opt_AllocSetFree(MemoryContext context, void* pointer); +static void* opt_AllocSetRealloc(MemoryContext context, void* pointer, Size align, Size size, const char* file, int line); +static void opt_AllocSetInit(MemoryContext context); +static void opt_AllocSetReset(MemoryContext context); +static void opt_AllocSetDelete(MemoryContext context); +static Size opt_AllocSetGetChunkSpace(MemoryContext context, void* pointer); +static bool opt_AllocSetIsEmpty(MemoryContext context); +static void opt_AllocSetStats(MemoryContext context, int level); + +/* + * Public routines + */ +static MemoryContextMethods AllocSetMethods = { + opt_AllocSetAlloc, + opt_AllocSetFree, + opt_AllocSetRealloc, + opt_AllocSetInit, + opt_AllocSetReset, + opt_AllocSetDelete, + opt_AllocSetGetChunkSpace, + opt_AllocSetIsEmpty, + opt_AllocSetStats +}; + +MemoryContextMethods *get_AllocSetMethods() +{ + return &AllocSetMethods; +} + +/* + * 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 + */ +MemoryContext opt_AllocSetContextCreate(MemoryContext parent, const char* name, Size minContextSize, + Size initBlockSize, Size maxBlockSize) +{ + AllocSet context; + AllocBlock block; + int freeListIndex; + Size firstBlockSize; + + if (minContextSize == ALLOCSET_DEFAULT_MINSIZE && + initBlockSize == ALLOCSET_DEFAULT_INITSIZE) + freeListIndex = 0; + else if (minContextSize == ALLOCSET_SMALL_MINSIZE && + initBlockSize == ALLOCSET_SMALL_INITSIZE) + freeListIndex = 1; + else + freeListIndex = -1; + + if (freeListIndex >= 0) + { + AllocSetFreeList *freelist = &context_freelists[freeListIndex]; + + if (freelist->first_free != NULL) + { + /* Remove entry from freelist */ + context = freelist->first_free; + freelist->first_free = (AllocSet) context->header.nextchild; + freelist->num_free--; + + /* Update its maxBlockSize; everything else should be OK */ + context->maxBlockSize = maxBlockSize; + + /* Reinitialize its header, installing correct name and parent */ + opt_MemoryContextCreate((MemoryContext) context, + T_OptAllocSetContext, + &AllocSetMethods, + parent, + name); + + context->maxSpaceSize = SELF_GENRIC_MEMCTX_LIMITATION; + return (MemoryContext) context; + } + } + + /* Determine size of initial block */ + firstBlockSize = MAXALIGN(sizeof(AllocSetContext)) + + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + if (minContextSize != 0) + firstBlockSize = Max(firstBlockSize, minContextSize); + else + firstBlockSize = Max(firstBlockSize, initBlockSize); + + context = (AllocSet) malloc(firstBlockSize); + if (context == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_MEMORY), + errmsg("out of memory"), + errdetail("Failed while creating memory context \"%s\".", + name))); + + block = (AllocBlock) (((char *) context) + MAXALIGN(sizeof(AllocSetContext))); + block->aset = context; + block->freeptr = ((char*)block) + ALLOC_BLOCKHDRSZ; + block->endptr = ((char*)context) + firstBlockSize; + block->allocSize = firstBlockSize; + + context->totalSpace += firstBlockSize; + context->freeSpace += block->endptr - block->freeptr; + + block->prev = NULL; + block->next = NULL; + + /* Remember block as part of block list */ + context->blocks = block; + /* Mark block as not to be released at reset time */ + context->keeper = block; + + MemSetAligned(context->freelist, 0, sizeof(context->freelist)); + + context->initBlockSize = initBlockSize; + context->maxBlockSize = maxBlockSize; + context->nextBlockSize = initBlockSize; + context->freeListIndex = freeListIndex; + + context->allocChunkLimit = ALLOC_CHUNK_LIMIT; + while ((Size)(context->allocChunkLimit + ALLOC_CHUNKHDRSZ) > + (Size)((maxBlockSize - ALLOC_BLOCKHDRSZ) / ALLOC_CHUNK_FRACTION)) { + context->allocChunkLimit >>= 1; + } + + opt_MemoryContextCreate((MemoryContext) context, + T_OptAllocSetContext, + &AllocSetMethods, + parent, + name); + + context->maxSpaceSize = SELF_GENRIC_MEMCTX_LIMITATION; + + 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 void opt_AllocSetInit(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. + */ +static void opt_AllocSetReset(MemoryContext context) +{ + AllocSet set = (AllocSet)context; + AllocBlock block; + + AssertArg(AllocSetIsValid(set)); + + /* 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; + Size tempSize = block->allocSize; + + if (block == set->keeper) { + /* Reset the block, but don't return it to malloc */ + char* datastart = ((char*)block) + ALLOC_BLOCKHDRSZ; + + block->freeptr = datastart; + block->allocSize = tempSize; + block->next = NULL; + block->prev = NULL; + } else { + free(block); + } + block = next; + } + /* Reset block size allocation sequence, too */ + set->nextBlockSize = set->initBlockSize; + + if (set->blocks != NULL) { + /* calculate memory statisic after reset. */ + block = set->blocks; + + set->freeSpace = block->endptr - block->freeptr; + set->totalSpace = block->endptr - (char*)block; + } else { + set->freeSpace = 0; + set->totalSpace = 0; + } +} + +/* + * AllocSetDelete + * Frees all memory which is allocated in the given set, + * in preparation for deletion of the set. + * + * Unlike AllocSetReset, this *must* free all resources of the set. + * But note we are not responsible for deleting the context node itself. + */ +static void opt_AllocSetDelete(MemoryContext context) +{ + AllocSet set = (AllocSet)context; + AssertArg(AllocSetIsValid(set)); + + AllocBlock block = set->blocks; + + if (set->freeListIndex >= 0) + { + AllocSetFreeList *freelist = &context_freelists[set->freeListIndex]; + + if (!context->isReset) { + context->methods->reset(context); + context->isReset = true; + } + + if (freelist->num_free >= MAX_FREE_CONTEXTS) + { + while (freelist->first_free != NULL) + { + AllocSetContext *oldset = freelist->first_free; + + freelist->first_free = (AllocSetContext *) oldset->header.nextchild; + freelist->num_free--; + + /* All that remains is to free the header/initial block */ + free(oldset); + } + Assert(freelist->num_free == 0); + } + + /* Now add the just-deleted context to the freelist. */ + set->header.nextchild = (MemoryContext) freelist->first_free; + freelist->first_free = set; + freelist->num_free++; + + return; + } + + while (block != NULL) { + AllocBlock next = block->next; + + if (block != set->keeper) + free(block); + + block = next; + } + + free(set); +} + +/* + * AllocSetAlloc + * Returns pointer to allocated memory of given size; memory is added + * to the set. + */ +static void* opt_AllocSetAlloc(MemoryContext context, Size align, Size size, const char* file, int line) +{ + Assert(file != NULL); + Assert(line != 0); + AllocSet set = (AllocSet)context; + AllocBlock block; + AllocChunk chunk; +#ifndef ENABLE_MEMORY_CHECK + unsigned int fidx; +#endif + Size chunk_size; + Size blksize; + + AssertArg(AllocSetIsValid(set)); + AssertArg(align == 0); + + size += ALLOC_MAGICHDRSZ; + + /* + * If requested size exceeds maximum for chunks, allocate an entire block + * for this request. + */ +#ifndef ENABLE_MEMORY_CHECK + if (size > set->allocChunkLimit) { +#endif + chunk_size = MAXALIGN(size); + blksize = chunk_size + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + + block = (AllocBlock) malloc(blksize); + if (block == NULL) { + return NULL; + } + block->aset = set; + block->freeptr = block->endptr = ((char*)block) + blksize; + block->allocSize = blksize; + + /* enlarge total space only. */ + set->totalSpace += blksize; + + chunk = (AllocChunk)(((char*)block) + ALLOC_BLOCKHDRSZ); + chunk->aset = set; + chunk->size = chunk_size; + + /* + * 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->prev = set->blocks; + block->next = set->blocks->next; + if (block->next) + block->next->prev = block; + set->blocks->next = block; + } else { + block->prev = NULL; + block->next = NULL; + set->blocks = block; + } + + return AllocChunkGetPointer(chunk); +#ifndef ENABLE_MEMORY_CHECK + } + + /* + * 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 = opt_AllocSetFreeIndex(size); + chunk = set->freelist[fidx]; + if (chunk != NULL) { + Assert(chunk->size >= size); + Assert(chunk->aset != set); + Assert((int)fidx == opt_AllocSetFreeIndex(chunk->size)); + + set->freelist[fidx] = (AllocChunk)chunk->aset; + + chunk->aset = (void*)set; + + set->freeSpace -= (chunk->size + ALLOC_CHUNKHDRSZ); + + return AllocChunkGetPointer(chunk); + } + + /* + * Choose the actual chunk size to allocate. + */ + chunk_size = ((unsigned long)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 = opt_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 << ((unsigned int)a_fidx + ALLOC_MINBITS))) { + a_fidx--; + Assert(a_fidx >= 0); + availchunk = ((Size)1 << ((unsigned int)a_fidx + ALLOC_MINBITS)); + } + + chunk = (AllocChunk)(block->freeptr); + + block->freeptr += (availchunk + ALLOC_CHUNKHDRSZ); + availspace -= (availchunk + ALLOC_CHUNKHDRSZ); + + chunk->size = availchunk; + 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); + if (block == NULL) { + return NULL; + } + + block->aset = set; + block->freeptr = ((char*)block) + ALLOC_BLOCKHDRSZ; + block->endptr = ((char*)block) + blksize; + block->allocSize = blksize; + + set->totalSpace += blksize; + set->freeSpace += blksize - ALLOC_BLOCKHDRSZ; + + /* + * 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->prev = NULL; + block->next = set->blocks; + if (block->next) + block->next->prev = block; + set->blocks = block; + } + + /* + * OK, do the allocation + */ + chunk = (AllocChunk)(block->freeptr); + + block->freeptr += (chunk_size + ALLOC_CHUNKHDRSZ); + + set->freeSpace -= (chunk_size + ALLOC_CHUNKHDRSZ); + + Assert(block->freeptr <= block->endptr); + + chunk->aset = (void*)set; + chunk->size = chunk_size; + + return AllocChunkGetPointer(chunk); +#endif +} + +/* + * AllocSetFree + * Frees allocated memory; memory is removed from the set. + */ +static void opt_AllocSetFree(MemoryContext context, void* pointer) +{ + AllocSet set = (AllocSet)context; + AllocChunk chunk = AllocPointerGetChunk(pointer); + Size tempSize = 0; + + AssertArg(AllocSetIsValid(set)); + +#ifndef ENABLE_MEMORY_CHECK + if (chunk->size > set->allocChunkLimit) { +#endif + /* + * Big chunks are certain to have been allocated as single-chunk + * blocks. Find the containing block and return it to malloc(). + */ + AllocBlock block = (AllocBlock)(((char*)chunk) - ALLOC_BLOCKHDRSZ); + + + /* OK, remove block from aset's list and free it */ + if (block->prev) + block->prev->next = block->next; + else + set->blocks = block->next; + + if (block->next) + block->next->prev = block->prev; + + tempSize = block->allocSize; + + set->totalSpace -= block->allocSize; + + /* clean the structure of block */ + block->aset = NULL; + block->prev = NULL; + block->next = NULL; + block->freeptr = NULL; + block->endptr = NULL; + block->allocSize = 0; + + free(block); +#ifndef ENABLE_MEMORY_CHECK + } else { + /* Normal case, put the chunk into appropriate freelist */ + int fidx = opt_AllocSetFreeIndex(chunk->size); + + chunk->aset = (void*)set->freelist[fidx]; + set->freeSpace += chunk->size + ALLOC_CHUNKHDRSZ; + + set->freelist[fidx] = chunk; + Assert(chunk->aset != set); + } +#endif +} + +/* + * AllocSetRealloc + * Returns new pointer to allocated memory of given size; this memory + * is added to the set. Memory associated with given pointer is copied + * into the new memory, and the old memory is freed. + */ +static void* opt_AllocSetRealloc(MemoryContext context, void* pointer, Size align, Size size, const char* file, int line) +{ + AllocSet set = (AllocSet)context; + AllocChunk chunk = AllocPointerGetChunk(pointer); + Size oldsize = chunk->size; + + AssertArg(AllocSetIsValid(set)); + AssertArg(align == 0); + + /* + * 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 + ALLOC_MAGICHDRSZ)) { + size += ALLOC_MAGICHDRSZ; + return pointer; + } + +#ifndef ENABLE_MEMORY_CHECK + if (oldsize > set->allocChunkLimit) +#endif + { + /* + * 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 = (AllocBlock)(((char*)chunk) - ALLOC_BLOCKHDRSZ); + AllocBlock oldBlock = NULL; + Size chksize; + Size blksize; + + size += ALLOC_MAGICHDRSZ; + + /* + * Try to verify that we have a sane block pointer: it should + * reference the correct aset, and freeptr and endptr should point + * just past the chunk. + */ + + /* Do the realloc */ + chksize = MAXALIGN(size); + blksize = chksize + ALLOC_BLOCKHDRSZ + ALLOC_CHUNKHDRSZ; + oldBlock = block; + oldsize = block->allocSize; + + block = (AllocBlock) realloc(block, blksize); + if (block == NULL) { + return NULL; + } + block->freeptr = block->endptr = ((char*)block) + blksize; + block->allocSize = blksize; + + set->totalSpace += blksize - oldsize; + + /* Update pointers since block has likely been moved */ + chunk = (AllocChunk)(((char*)block) + ALLOC_BLOCKHDRSZ); + if (block->prev) + block->prev->next = block; + else + set->blocks = block; + if (block->next) + block->next->prev = block; + chunk->size = chksize; + + return AllocChunkGetPointer(chunk); + } +#ifndef ENABLE_MEMORY_CHECK + 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 = + opt_AllocSetAlloc((MemoryContext)set, align, size, __FILE__, __LINE__); + + /* leave immediately if request was not completed */ + if (newPointer == NULL) + return NULL; + + /* transfer existing data (certain to fit) */ + memcpy(newPointer, pointer, oldsize); + + /* free old chunk */ + opt_AllocSetFree((MemoryContext)set, pointer); + + return newPointer; + } +#endif +} + +/* + * AllocSetGetChunkSpace + * Given a currently-allocated chunk, determine the total space + * it occupies (including all memory-allocation overhead). + */ +static Size opt_AllocSetGetChunkSpace(MemoryContext context, void* pointer) +{ + AllocChunk chunk = AllocPointerGetChunk(pointer); + + return chunk->size + ALLOC_CHUNKHDRSZ; +} + +/* + * AllocSetIsEmpty + * Is an allocset empty of any allocated space? + */ +static bool opt_AllocSetIsEmpty(MemoryContext context) +{ + /* + * 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; +} + +/* + * AllocSetStats + * Displays stats about memory consumption of an allocset. + */ +static void opt_AllocSetStats(MemoryContext context, int level) +{ + 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: %ld total in %ld blocks; %ld free (%ld chunks); %ld used\n", + set->header.name, + totalspace, + nblocks, + freespace, + nchunks, + totalspace - freespace); +} + diff --git a/src/common/backend/utils/mmgr/opt_mcxt.cpp b/src/common/backend/utils/mmgr/opt_mcxt.cpp new file mode 100644 index 000000000..5d6faf7ce --- /dev/null +++ b/src/common/backend/utils/mmgr/opt_mcxt.cpp @@ -0,0 +1,336 @@ +/* ------------------------------------------------------------------------- + * + * std_mcxt.cpp + * openGauss memory context management code. + * + * This module handles context management operations that are independent + * of the particular kind of context being operated on. It calls + * context-type-specific operations via the function pointers in a + * context's MemoryContextMethods struct. + * + * + * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/utils/mmgr/mcxt.c + * + * ------------------------------------------------------------------------- + */ + +#include "postgres.h" +#include "knl/knl_variable.h" +#include "postmaster/postmaster.h" +#include "utils/memutils.h" +#include "utils/memtrace.h" +#include "pgxc/pgxc.h" + +#include "miscadmin.h" +#include "storage/proc.h" +#include "storage/cstore/cstore_mem_alloc.h" +#include "threadpool/threadpool.h" +#include "tcop/tcopprot.h" +#include "workload/workload.h" +#include "pgstat.h" + +/***************************************************************************** + * GLOBAL MEMORY * + *****************************************************************************/ + +void* opt_MemoryAllocFromContext(MemoryContext context, Size size) +{ + void* ret = NULL; + + context->isReset = false; + + ret = (*context->methods->alloc)(context, 0, size, __FILE__, __LINE__); + if (ret == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY), + errmsg("memory is temporarily unavailable"), + errdetail("Failed on request of size %lu bytes under queryid %lu.", + (unsigned long)size, + u_sess->debug_query_id))); + + return ret; +} + +static void opt_TagMemoryContextSessionId(MemoryContext node, MemoryContext parent) +{ + uint64 sessId = 0; + if (parent) { + if (parent->session_id > 0) { + if (u_sess && u_sess->session_id > 0) { + Assert(parent->is_shared || parent->session_id == u_sess->session_id); + } + sessId = parent->session_id; + } else { + Assert(parent->is_shared || parent->thread_id == gs_thread_self()); + } + } else { + if (u_sess && u_sess->session_id) + sessId = u_sess->session_id; + } + + node->session_id = sessId; +} + +/* -------------------- + * MemoryContextCreate + * Context-type-independent part of context creation. + * + * This is only intended to be called by context-type-specific + * context creation routines, not by the unwashed masses. + * + * The context creation procedure is a little bit tricky because + * we want to be sure that we don't leave the context tree invalid + * in case of failure (such as insufficient memory to allocate the + * context node itself). The procedure goes like this: + * 1. Context-type-specific routine first calls MemoryContextCreate(), + * passing the appropriate tag/size/methods values (the methods + * pointer will ordinarily point to statically allocated data). + * The parent and name parameters usually come from the caller. + * 2. MemoryContextCreate() attempts to allocate the context node, + * plus space for the name. If this fails we can ereport() with no + * damage done. + * 3. We fill in all of the type-independent MemoryContext fields. + * 4. We call the type-specific init routine (using the methods pointer). + * The init routine is required to make the node minimally valid + * with zero chance of failure --- it can't allocate more memory, + * for example. + * 5. Now we have a minimally valid node that can behave correctly + * when told to reset or delete itself. We link the node to its + * parent (if any), making the node part of the context tree. + * 6. We return to the context-type-specific routine, which finishes + * up type-specific initialization. This routine can now do things + * that might fail (like allocate more memory), so long as it's + * sure the node is left in a state that delete will handle. + * + * This protocol doesn't prevent us from leaking memory if step 6 fails + * during creation of a top-level context, since there's no parent link + * in that case. However, if you run out of memory while you're building + * a top-level context, you might as well go home anyway... + * + * Normally, the context node and the name are allocated from + * t_thrd.top_mem_cxt (NOT from the parent context, since the node must + * survive resets of its parent context!). However, this routine is itself + * used to create t_thrd.top_mem_cxt! If we see that t_thrd.top_mem_cxt is NULL, + * we assume we are creating t_thrd.top_mem_cxt and use malloc() to allocate + * the node. + * + * Note that the name field of a MemoryContext does not point to + * separately-allocated storage, so it should not be freed at context + * deletion. + * -------------------- + */ +MemoryContext opt_MemoryContextCreate(MemoryContext node, NodeTag tag, + MemoryContextMethods *methods, MemoryContext parent, const char* name) +{ + node->type = tag; + node->parent = NULL; /* for the moment */ + node->firstchild = NULL; + node->prevchild = NULL; + node->nextchild = NULL; + node->isReset = true; + node->is_sealed = false; + node->is_shared = false; + node->methods = methods; + node->name = (char *)name; + node->cell.data.ptr_value = (void*)node; + node->cell.next = NULL; + node->thread_id = gs_thread_self(); + opt_TagMemoryContextSessionId(node, parent); + + /* OK to link node to parent (if any) */ + /* Could use MemoryContextSetParent here, but doesn't seem worthwhile */ + if (parent) { + node->parent = parent; + node->nextchild = parent->firstchild; + if (parent->firstchild != NULL) + parent->firstchild->prevchild = node; + parent->firstchild = node; + + node->level = parent->level + 1; + } else { + node->nextchild = NULL; + node->level = 0; + } + + /* Return to type-specific creation routine to finish up */ + return node; +} + +void* opt_MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line) +{ + void* ret = NULL; + + AssertArg(IsOptAllocSetContext(context)); + + context->isReset = false; + + ret = (*context->methods->alloc)(context, 0, size, file, line); + if (ret == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY), + errmsg("memory is temporarily unavailable"), + errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.", + (unsigned long)size, + u_sess->debug_query_id, + file, + line))); + + MemSetLoop(ret, 0, size); + + return ret; +} + +void* opt_MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line) +{ + void* ret = NULL; + + AssertArg(IsOptAllocSetContext(context)); + + context->isReset = false; + + ret = (*context->methods->alloc)(context, 0, size, file, line); + if (ret == NULL) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY), + errmsg("memory is temporarily unavailable"), + errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.", + (unsigned long)size, + u_sess->debug_query_id, + file, + line))); + + MemSetAligned(ret, 0, size); + + return ret; +} + +void opt_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent) +{ + AssertArg(IsOptAllocSetContext(context)); + AssertArg(context != new_parent); + + if (new_parent == context->parent) + return; + + /* Delink from existing parent, if any */ + if (context->parent) { + MemoryContext parent = context->parent; + + if (context->prevchild != NULL) + context->prevchild->nextchild = context->nextchild; + else { + Assert(parent->firstchild == context); + parent->firstchild = context->nextchild; + } + + if (context->nextchild != NULL) + context->nextchild->prevchild = context->prevchild; + } + + /* And relink */ + if (new_parent) { + AssertArg(IsOptAllocSetContext(new_parent)); + context->parent = new_parent; + context->prevchild = NULL; + context->nextchild = new_parent->firstchild; + if (new_parent->firstchild != NULL) + new_parent->firstchild->prevchild = context; + new_parent->firstchild = context; + } else { + context->parent = NULL; + context->prevchild = NULL; + context->nextchild = NULL; + } +} + +void opt_MemoryContextDeleteInternal(MemoryContext context, List *context_list) +{ + AssertArg(IsOptAllocSetContext(context)); + + /* save a function call in common case where there are no children */ + if (context->firstchild != NULL) + MemoryContextDeleteChildren(context, context_list); + + opt_MemoryContextSetParent(context, NULL); + + context->methods->delete_context(context); +} + +void opt_MemoryContextReset(MemoryContext context) +{ + AssertArg(IsOptAllocSetContext(context)); + + /* save a function call in common case where there are no children */ + if (context->firstchild != NULL) + MemoryContextResetChildren(context); + + /* Nothing to do if no pallocs since startup or last reset */ + if (!context->isReset) { + (*context->methods->reset)(context); + context->isReset = true; + } +} + +/* + * pfree + * Release an allocated chunk. + */ +void +opt_pfree(void *pointer) +{ + MemoryContext context = GetMemoryChunkContext(pointer); + + context->methods->free_p(context, pointer); +} + +void* +opt_repallocDebug(void* pointer, Size size, const char *file, int line) +{ + MemoryContext context; + void* ret = NULL; + + Assert(pointer != NULL); + Assert(pointer == (void*)MAXALIGN(pointer)); + + context = GetMemoryChunkContext(pointer); + + /* isReset must be false already */ + Assert(!context->isReset); + + ret = (*context->methods->realloc)(context, pointer, 0, size, file, line); + if (unlikely(ret == NULL)) + ereport(ERROR, + (errcode(ERRCODE_OUT_OF_LOGICAL_MEMORY), + errmsg("memory is temporarily unavailable"), + errdetail("Failed on request of size %lu bytes under queryid %lu in %s:%d.", + (unsigned long)size, + u_sess->debug_query_id, + file, + line))); + + return ret; +} + +void* +opt_repalloc_noexcept_Debug(void* pointer, Size size, const char *file, int line) +{ + MemoryContext context; + void* ret = NULL; + + Assert(pointer != NULL); + Assert(pointer == (void*)MAXALIGN(pointer)); + + context = GetMemoryChunkContext(pointer); + + /* isReset must be false already */ + Assert(!context->isReset); + + ret = (*context->methods->realloc)(context, pointer, 0, size, file, line); + + return ret; +} diff --git a/src/include/gtm/utils/palloc.h b/src/include/gtm/utils/palloc.h index eda1ba370..8b6165a55 100644 --- a/src/include/gtm/utils/palloc.h +++ b/src/include/gtm/utils/palloc.h @@ -68,6 +68,7 @@ extern void* MemoryContextAllocZeroAligned(MemoryContext context, Size size); : MemoryContextAllocZero(CurrentMemoryContext, sz)) extern void pfree(void* pointer); +extern void opt_pfree(void* pointer); extern void* repalloc(void* pointer, Size size); diff --git a/src/include/knl/knl_guc/knl_instance_attr_memory.h b/src/include/knl/knl_guc/knl_instance_attr_memory.h index e0e14e20f..b04a89362 100644 --- a/src/include/knl/knl_guc/knl_instance_attr_memory.h +++ b/src/include/knl/knl_guc/knl_instance_attr_memory.h @@ -47,6 +47,7 @@ typedef struct knl_instance_attr_memory { int max_process_memory; bool enable_memory_context_check_debug; int global_syscache_threshold; + bool disable_memory_stats; } knl_instance_attr_memory; #endif /* SRC_INCLUDE_KNL_KNL_INSTANCE_ATTR_MEMORY_H_ */ diff --git a/src/include/nodes/memnodes.h b/src/include/nodes/memnodes.h index 26924e59a..92252c55a 100644 --- a/src/include/nodes/memnodes.h +++ b/src/include/nodes/memnodes.h @@ -148,6 +148,7 @@ typedef struct AllocSetContext { /* maximum memory allocation of MemoryContext.For more information,we could see @StackSetContext too. */ Size maxSpaceSize; + int freeListIndex; MemoryTrack track; /* used to track the memory allocation information */ } AllocSetContext; @@ -278,6 +279,9 @@ extern MemoryProtectFuncDef GenericFunctions; extern MemoryProtectFuncDef SessionFunctions; extern MemoryProtectFuncDef SharedFunctions; +#define IsOptAllocSetContext(cxt) \ + ((cxt) != NULL && (IsA((cxt), OptAllocSetContext))) + /* * MemoryContextIsValid * True iff memory context is valid. @@ -288,7 +292,7 @@ extern MemoryProtectFuncDef SharedFunctions; ((context) != NULL && \ (IsA((context), AllocSetContext) || IsA((context), AsanSetContext) || IsA((context), StackAllocSetContext) || \ IsA((context), SharedAllocSetContext) || IsA((context), MemalignAllocSetContext) || \ - IsA((context), MemalignSharedAllocSetContext))) + IsA((context), MemalignSharedAllocSetContext) || IsA((context), OptAllocSetContext))) #define AllocSetContextUsedSpace(aset) ((aset)->totalSpace - (aset)->freeSpace) diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 9136dbb9a..b26a1f64a 100755 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -359,6 +359,7 @@ typedef enum NodeTag { */ T_MemoryContext = 600, T_AllocSetContext, + T_OptAllocSetContext, T_AsanSetContext, T_StackAllocSetContext, T_SharedAllocSetContext, diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index f8ef78a88..68aa64d66 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -94,12 +94,15 @@ extern THR_LOCAL PGDLLIMPORT MemoryContext ErrorContext; */ extern void MemoryContextInit(void); extern void MemoryContextReset(MemoryContext context); +extern void opt_MemoryContextReset(MemoryContext context); extern void MemoryContextDelete(MemoryContext context); +extern void opt_MemoryContextDeleteInternal(MemoryContext context, List* context_list = NULL); extern void MemoryContextResetChildren(MemoryContext context); extern void MemoryContextDeleteChildren(MemoryContext context, List* context_list = NULL); extern void MemoryContextDestroyAtThreadExit(MemoryContext context); extern void MemoryContextResetAndDeleteChildren(MemoryContext context); extern void MemoryContextSetParent(MemoryContext context, MemoryContext new_parent); +extern void opt_MemoryContextSetParent(MemoryContext context, MemoryContext new_parent); extern Size GetMemoryChunkSpace(void* pointer); extern MemoryContext GetMemoryChunkContext(void* pointer); extern MemoryContext MemoryContextGetParent(MemoryContext context); @@ -131,6 +134,8 @@ extern MemoryContext MemoryContextCreate( extern MemoryContext MemoryContextCreate( NodeTag tag, Size size, MemoryContextMethods* methods, MemoryContext parent, const char* name); +extern MemoryContext opt_MemoryContextCreate(MemoryContext node, NodeTag tag, + MemoryContextMethods *methods, MemoryContext parent, const char* name); /* * Memory-context-type-specific functions */ @@ -140,6 +145,8 @@ extern MemoryContext MemoryContextCreate( extern MemoryContext AllocSetContextCreate(MemoryContext parent, const char* name, Size minContextSize, Size initBlockSize, Size maxBlockSize, MemoryContextType type = STANDARD_CONTEXT, Size maxSize = DEFAULT_MEMORY_CONTEXT_MAX_SIZE, bool isSession = false); +extern MemoryContext opt_AllocSetContextCreate(MemoryContext parent, + const char* name, Size minContextSize, Size initBlockSize, Size maxBlockSize); /* * Recommended default alloc parameters, suitable for "ordinary" contexts diff --git a/src/include/utils/palloc.h b/src/include/utils/palloc.h index fa9bd4d48..a56dc5700 100644 --- a/src/include/utils/palloc.h +++ b/src/include/utils/palloc.h @@ -87,12 +87,15 @@ extern THR_LOCAL PGDLLIMPORT MemoryContext TopMemoryContext; * Fundamental memory-allocation operations (more are in utils/memutils.h) */ extern void* MemoryAllocFromContext(MemoryContext context, Size size, const char* file, int line); +extern void* opt_MemoryAllocFromContext(MemoryContext context, Size size); extern void* MemoryContextAllocDebug(MemoryContext context, Size size, const char* file, int line); extern void* MemoryContextAllocHugeDebug(MemoryContext context, Size size, const char* file, int line); extern void* repallocHugeDebug(void* pointer, Size size, const char* file, int line); extern void* MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line); +extern void* opt_MemoryContextAllocZeroDebug(MemoryContext context, Size size, const char* file, int line); extern void* MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line); extern void* MemoryContextAllocExtendedDebug(MemoryContext context, Size size, int flags, const char* file, int line); +extern void* opt_MemoryContextAllocZeroAlignedDebug(MemoryContext context, Size size, const char* file, int line); extern char* MemoryContextStrdupDebug(MemoryContext context, const char* string, const char* file, int line); extern void* MemoryContextMemalignAllocDebug(MemoryContext context, Size align, Size size, const char* file, int line); extern void MemoryContextMemalignFree(MemoryContext context, void* pointer); @@ -135,6 +138,7 @@ extern THR_LOCAL MemoryContext AlignMemoryContext; : MemoryContextAllocZero(CurrentMemoryContext, sz)) extern void pfree(void* pointer); +extern void opt_pfree(void* pointer); template bool isConst(T& x) @@ -161,7 +165,9 @@ bool isConst(T const& x) #define pfree_ext(__p) FREE_POINTER(__p) extern void* repallocDebug(void* pointer, Size size, const char* file, int line); +extern void* opt_repallocDebug(void* pointer, Size size, const char* file, int line); extern void* repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int line); +extern void* opt_repalloc_noexcept_Debug(void* pointer, Size size, const char* file, int line); /* * MemoryContextSwitchTo can't be a macro in standard C compilers. diff --git a/src/test/regress/pg_regress.cpp b/src/test/regress/pg_regress.cpp index a89937d0b..caffcfeb0 100644 --- a/src/test/regress/pg_regress.cpp +++ b/src/test/regress/pg_regress.cpp @@ -5412,7 +5412,7 @@ static void CheckCleanCodeWarningInfo(const int baseNum, const int currentNum, return; } -#define BASE_GLOBAL_VARIABLE_NUM 220 +#define BASE_GLOBAL_VARIABLE_NUM 221 #define CMAKE_CMD_BUF_LEN 1000