1 /*-------------------------------------------------------------------------
4 * Allocation set definitions.
6 * AllocSet is our standard implementation of the abstract MemoryContext
10 * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
11 * Portions Copyright (c) 1994, Regents of the University of California
17 * This is a new (Feb. 05, 1999) implementation of the allocation set
18 * routines. AllocSet...() does not use OrderedSet...() any more.
19 * Instead it manages allocations in a block pool by itself, combining
20 * many small allocations in a few bigger blocks. AllocSetFree() normally
21 * doesn't free() memory really. It just add's the free'd area to some
22 * list for later reuse by AllocSetAlloc(). All memory blocks are free()'d
23 * at once on AllocSetReset(), which happens when the memory context gets
27 * Performance improvement from Tom Lane, 8/99: for extremely large request
28 * sizes, we do want to be able to give the memory back to free() as soon
29 * as it is pfree()'d. Otherwise we risk tying up a lot of memory in
30 * freelist entries that might never be usable. This is specially needed
31 * when the caller is repeatedly repalloc()'ing a block bigger and bigger;
32 * the previous instances of the block were guaranteed to be wasted until
33 * AllocSetReset() under the old way.
35 * Further improvement 12/00: as the code stood, request sizes in the
36 * midrange between "small" and "large" were handled very inefficiently,
37 * because any sufficiently large free chunk would be used to satisfy a
38 * request, even if it was much larger than necessary. This led to more
39 * and more wasted space in allocated chunks over time. To fix, get rid
40 * of the midrange behavior: we now handle only "small" power-of-2-size
41 * chunks as chunks. Anything "large" is passed off to malloc(). Change
42 * the number of freelists to change the small/large boundary.
45 * About CLOBBER_FREED_MEMORY:
47 * If this symbol is defined, all freed memory is overwritten with 0x7F's.
48 * This is useful for catching places that reference already-freed memory.
50 * About MEMORY_CONTEXT_CHECKING:
52 * Since we usually round request sizes up to the next power of 2, there
53 * is often some unused space immediately after a requested data area.
54 * Thus, if someone makes the common error of writing past what they've
55 * requested, the problem is likely to go unnoticed ... until the day when
56 * there *isn't* any wasted space, perhaps because of different memory
57 * alignment on a new platform, or some other effect. To catch this sort
58 * of problem, the MEMORY_CONTEXT_CHECKING option stores 0x7E just beyond
59 * the requested space whenever the request is less than the actual chunk
60 * size, and verifies that the byte is undamaged when the chunk is freed.
62 *-------------------------------------------------------------------------
67 #include "utils/memutils.h"
69 /* Define this to detail debug alloc information */
70 /* #define HAVE_ALLOCINFO */
72 /*--------------------
73 * Chunk freelist k holds chunks of size 1 << (k + ALLOC_MINBITS),
74 * for k = 0 .. ALLOCSET_NUM_FREELISTS-1.
76 * Note that all chunks in the freelists have power-of-2 sizes. This
77 * improves recyclability: we may waste some space, but the wasted space
78 * should stay pretty constant as requests are made and released.
80 * A request too large for the last freelist is handled by allocating a
81 * dedicated block from malloc(). The block still has a block header and
82 * chunk header, but when the chunk is freed we'll return the whole block
83 * to malloc(), not put it on our freelists.
85 * CAUTION: ALLOC_MINBITS must be large enough so that
86 * 1<<ALLOC_MINBITS is at least MAXALIGN,
87 * or we may fail to align the smallest chunks adequately.
88 * 8-byte alignment is enough on all currently known machines.
90 * With the current parameters, request sizes up to 8K are treated as chunks,
91 * larger requests go into dedicated blocks. Change ALLOCSET_NUM_FREELISTS
92 * to adjust the boundary point.
96 #define ALLOC_MINBITS 3 /* smallest chunk size is 8 bytes */
97 #define ALLOCSET_NUM_FREELISTS 11
98 #define ALLOC_CHUNK_LIMIT (1 << (ALLOCSET_NUM_FREELISTS-1+ALLOC_MINBITS))
99 /* Size of largest chunk that we use a fixed size for */
101 /*--------------------
102 * The first block allocated for an allocset has size initBlockSize.
103 * Each time we have to allocate another block, we double the block size
104 * (if possible, and without exceeding maxBlockSize), so as to reduce
105 * the bookkeeping load on malloc().
107 * Blocks allocated to hold oversize chunks do not follow this rule, however;
108 * they are just however big they need to be to hold that single chunk.
109 *--------------------
112 #define ALLOC_BLOCKHDRSZ MAXALIGN(sizeof(AllocBlockData))
113 #define ALLOC_CHUNKHDRSZ MAXALIGN(sizeof(AllocChunkData))
115 typedef struct AllocBlockData
*AllocBlock
; /* forward reference */
116 typedef struct AllocChunkData
*AllocChunk
;
120 * Aligned pointer which may be a member of an allocation set.
122 typedef void *AllocPointer
;
125 * AllocSetContext is our standard implementation of MemoryContext.
127 * Note: isReset means there is nothing for AllocSetReset to do. This is
128 * different from the aset being physically empty (empty blocks list) because
129 * we may still have a keeper block. It's also different from the set being
130 * logically empty, because we don't attempt to detect pfree'ing the last
133 typedef struct AllocSetContext
135 MemoryContextData header
; /* Standard memory-context fields */
136 /* Info about storage allocated in this context: */
137 AllocBlock blocks
; /* head of list of blocks in this set */
138 AllocChunk freelist
[ALLOCSET_NUM_FREELISTS
]; /* free chunk lists */
139 bool isReset
; /* T = no space alloced since last reset */
140 /* Allocation parameters for this context: */
141 Size initBlockSize
; /* initial block size */
142 Size maxBlockSize
; /* maximum block size */
143 Size nextBlockSize
; /* next block size to allocate */
144 Size allocChunkLimit
; /* effective chunk size limit */
145 AllocBlock keeper
; /* if not NULL, keep this block over resets */
148 typedef AllocSetContext
*AllocSet
;
152 * An AllocBlock is the unit of memory that is obtained by aset.c
153 * from malloc(). It contains one or more AllocChunks, which are
154 * the units requested by palloc() and freed by pfree(). AllocChunks
155 * cannot be returned to malloc() individually, instead they are put
156 * on freelists by pfree() and re-used by the next palloc() that has
157 * a matching request size.
159 * AllocBlockData is the header data for a block --- the usable space
160 * within the block begins at the next alignment boundary.
162 typedef struct AllocBlockData
164 AllocSet aset
; /* aset that owns this block */
165 AllocBlock next
; /* next block in aset's blocks list */
166 char *freeptr
; /* start of free space in this block */
167 char *endptr
; /* end of space in this block */
172 * The prefix of each piece of memory in an AllocBlock
174 * NB: this MUST match StandardChunkHeader as defined by utils/memutils.h.
176 typedef struct AllocChunkData
178 /* aset is the owning aset if allocated, or the freelist link if free */
180 /* size is always the size of the usable space in the chunk */
182 #ifdef MEMORY_CONTEXT_CHECKING
183 /* when debugging memory usage, also store actual requested size */
184 /* this is zero in a free chunk */
190 * AllocPointerIsValid
191 * True iff pointer is valid allocation pointer.
193 #define AllocPointerIsValid(pointer) PointerIsValid(pointer)
197 * True iff set is valid allocation set.
199 #define AllocSetIsValid(set) PointerIsValid(set)
201 #define AllocPointerGetChunk(ptr) \
202 ((AllocChunk)(((char *)(ptr)) - ALLOC_CHUNKHDRSZ))
203 #define AllocChunkGetPointer(chk) \
204 ((AllocPointer)(((char *)(chk)) + ALLOC_CHUNKHDRSZ))
207 * These functions implement the MemoryContext API for AllocSet contexts.
209 static void *AllocSetAlloc(MemoryContext context
, Size size
);
210 static void AllocSetFree(MemoryContext context
, void *pointer
);
211 static void *AllocSetRealloc(MemoryContext context
, void *pointer
, Size size
);
212 static void AllocSetInit(MemoryContext context
);
213 static void AllocSetReset(MemoryContext context
);
214 static void AllocSetDelete(MemoryContext context
);
215 static Size
AllocSetGetChunkSpace(MemoryContext context
, void *pointer
);
216 static bool AllocSetIsEmpty(MemoryContext context
);
217 static void AllocSetStats(MemoryContext context
, int level
);
219 #ifdef MEMORY_CONTEXT_CHECKING
220 static void AllocSetCheck(MemoryContext context
);
224 * This is the virtual function table for AllocSet contexts.
226 static MemoryContextMethods AllocSetMethods
= {
233 AllocSetGetChunkSpace
,
236 #ifdef MEMORY_CONTEXT_CHECKING
246 #ifdef HAVE_ALLOCINFO
247 #define AllocFreeInfo(_cxt, _chunk) \
248 fprintf(stderr, "AllocFree: %s: %p, %d\n", \
249 (_cxt)->header.name, (_chunk), (_chunk)->size)
250 #define AllocAllocInfo(_cxt, _chunk) \
251 fprintf(stderr, "AllocAlloc: %s: %p, %d\n", \
252 (_cxt)->header.name, (_chunk), (_chunk)->size)
254 #define AllocFreeInfo(_cxt, _chunk)
255 #define AllocAllocInfo(_cxt, _chunk)
259 * AllocSetFreeIndex -
261 * Depending on the size of an allocation compute which freechunk
262 * list of the alloc set it belongs to. Caller must have verified
263 * that size <= ALLOC_CHUNK_LIMIT.
267 AllocSetFreeIndex(Size size
)
273 size
= (size
- 1) >> ALLOC_MINBITS
;
279 Assert(idx
< ALLOCSET_NUM_FREELISTS
);
285 #ifdef RANDOMIZE_ALLOCATED_MEMORY
288 * Fill a just-allocated piece of memory with "random" data. It's not really
289 * very random, just a repeating sequence with a length that's prime. What
290 * we mainly want out of it is to have a good probability that two palloc's
291 * of the same number of bytes start out containing different data.
294 randomize_mem(char *ptr
, size_t size
)
296 static int save_ctr
= 1;
308 #endif /* RANDOMIZE_ALLOCATED_MEMORY */
317 * AllocSetContextCreate
318 * Create a new AllocSet context.
320 * parent: parent context, or NULL if top-level context
321 * name: name of context (for debugging --- string will be copied)
322 * minContextSize: minimum context size
323 * initBlockSize: initial allocation block size
324 * maxBlockSize: maximum allocation block size
327 AllocSetContextCreate(MemoryContext parent
,
335 /* Do the type-independent part of context creation */
336 context
= (AllocSet
) MemoryContextCreate(T_AllocSetContext
,
337 sizeof(AllocSetContext
),
343 * Make sure alloc parameters are reasonable, and save them.
345 * We somewhat arbitrarily enforce a minimum 1K block size.
347 initBlockSize
= MAXALIGN(initBlockSize
);
348 if (initBlockSize
< 1024)
349 initBlockSize
= 1024;
350 maxBlockSize
= MAXALIGN(maxBlockSize
);
351 if (maxBlockSize
< initBlockSize
)
352 maxBlockSize
= initBlockSize
;
353 context
->initBlockSize
= initBlockSize
;
354 context
->maxBlockSize
= maxBlockSize
;
355 context
->nextBlockSize
= initBlockSize
;
358 * Compute the allocation chunk size limit for this context. It can't be
359 * more than ALLOC_CHUNK_LIMIT because of the fixed number of freelists.
360 * If maxBlockSize is small then requests exceeding the maxBlockSize
361 * should be treated as large chunks, too. We have to have
362 * allocChunkLimit a power of two, because the requested and
363 * actually-allocated sizes of any chunk must be on the same side of the
364 * limit, else we get confused about whether the chunk is "big".
366 context
->allocChunkLimit
= ALLOC_CHUNK_LIMIT
;
367 while (context
->allocChunkLimit
>
368 (Size
) (maxBlockSize
- ALLOC_BLOCKHDRSZ
- ALLOC_CHUNKHDRSZ
))
369 context
->allocChunkLimit
>>= 1;
372 * Grab always-allocated space, if requested
374 if (minContextSize
> ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
)
376 Size blksize
= MAXALIGN(minContextSize
);
379 block
= (AllocBlock
) malloc(blksize
);
382 MemoryContextStats(TopMemoryContext
);
384 (errcode(ERRCODE_OUT_OF_MEMORY
),
385 errmsg("out of memory"),
386 errdetail("Failed while creating memory context \"%s\".",
389 block
->aset
= context
;
390 block
->freeptr
= ((char *) block
) + ALLOC_BLOCKHDRSZ
;
391 block
->endptr
= ((char *) block
) + blksize
;
392 block
->next
= context
->blocks
;
393 context
->blocks
= block
;
394 /* Mark block as not to be released at reset time */
395 context
->keeper
= block
;
398 context
->isReset
= true;
400 return (MemoryContext
) context
;
405 * Context-type-specific initialization routine.
407 * This is called by MemoryContextCreate() after setting up the
408 * generic MemoryContext fields and before linking the new context
409 * into the context tree. We must do whatever is needed to make the
410 * new context minimally valid for deletion. We must *not* risk
411 * failure --- thus, for example, allocating more memory is not cool.
412 * (AllocSetContextCreate can allocate memory when it gets control
416 AllocSetInit(MemoryContext context
)
419 * Since MemoryContextCreate already zeroed the context node, we don't
420 * have to do anything here: it's already OK.
426 * Frees all memory which is allocated in the given set.
428 * Actually, this routine has some discretion about what to do.
429 * It should mark all allocated chunks freed, but it need not necessarily
430 * give back all the resources the set owns. Our actual implementation is
431 * that we hang onto any "keeper" block specified for the set. In this way,
432 * we don't thrash malloc() when a context is repeatedly reset after small
433 * allocations, which is typical behavior for per-tuple contexts.
436 AllocSetReset(MemoryContext context
)
438 AllocSet set
= (AllocSet
) context
;
441 AssertArg(AllocSetIsValid(set
));
443 /* Nothing to do if no pallocs since startup or last reset */
447 #ifdef MEMORY_CONTEXT_CHECKING
448 /* Check for corruption and leaks before freeing */
449 AllocSetCheck(context
);
452 /* Clear chunk freelists */
453 MemSetAligned(set
->freelist
, 0, sizeof(set
->freelist
));
457 /* New blocks list is either empty or just the keeper block */
458 set
->blocks
= set
->keeper
;
460 while (block
!= NULL
)
462 AllocBlock next
= block
->next
;
464 if (block
== set
->keeper
)
466 /* Reset the block, but don't return it to malloc */
467 char *datastart
= ((char *) block
) + ALLOC_BLOCKHDRSZ
;
469 #ifdef CLOBBER_FREED_MEMORY
470 /* Wipe freed memory for debugging purposes */
471 memset(datastart
, 0x7F, block
->freeptr
- datastart
);
473 block
->freeptr
= datastart
;
478 /* Normal case, release the block */
479 #ifdef CLOBBER_FREED_MEMORY
480 /* Wipe freed memory for debugging purposes */
481 memset(block
, 0x7F, block
->freeptr
- ((char *) block
));
488 /* Reset block size allocation sequence, too */
489 set
->nextBlockSize
= set
->initBlockSize
;
496 * Frees all memory which is allocated in the given set,
497 * in preparation for deletion of the set.
499 * Unlike AllocSetReset, this *must* free all resources of the set.
500 * But note we are not responsible for deleting the context node itself.
503 AllocSetDelete(MemoryContext context
)
505 AllocSet set
= (AllocSet
) context
;
506 AllocBlock block
= set
->blocks
;
508 AssertArg(AllocSetIsValid(set
));
510 #ifdef MEMORY_CONTEXT_CHECKING
511 /* Check for corruption and leaks before freeing */
512 AllocSetCheck(context
);
515 /* Make it look empty, just in case... */
516 MemSetAligned(set
->freelist
, 0, sizeof(set
->freelist
));
520 while (block
!= NULL
)
522 AllocBlock next
= block
->next
;
524 #ifdef CLOBBER_FREED_MEMORY
525 /* Wipe freed memory for debugging purposes */
526 memset(block
, 0x7F, block
->freeptr
- ((char *) block
));
535 * Returns pointer to allocated memory of given size; memory is added
539 AllocSetAlloc(MemoryContext context
, Size size
)
541 AllocSet set
= (AllocSet
) context
;
548 AssertArg(AllocSetIsValid(set
));
551 * If requested size exceeds maximum for chunks, allocate an entire block
554 if (size
> set
->allocChunkLimit
)
556 chunk_size
= MAXALIGN(size
);
557 blksize
= chunk_size
+ ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
;
558 block
= (AllocBlock
) malloc(blksize
);
561 MemoryContextStats(TopMemoryContext
);
563 (errcode(ERRCODE_OUT_OF_MEMORY
),
564 errmsg("out of memory"),
565 errdetail("Failed on request of size %lu.",
566 (unsigned long) size
)));
569 block
->freeptr
= block
->endptr
= ((char *) block
) + blksize
;
571 chunk
= (AllocChunk
) (((char *) block
) + ALLOC_BLOCKHDRSZ
);
573 chunk
->size
= chunk_size
;
574 #ifdef MEMORY_CONTEXT_CHECKING
575 chunk
->requested_size
= size
;
576 /* set mark to catch clobber of "unused" space */
577 if (size
< chunk_size
)
578 ((char *) AllocChunkGetPointer(chunk
))[size
] = 0x7E;
580 #ifdef RANDOMIZE_ALLOCATED_MEMORY
581 /* fill the allocated space with junk */
582 randomize_mem((char *) AllocChunkGetPointer(chunk
), size
);
586 * Stick the new block underneath the active allocation block, so that
587 * we don't lose the use of the space remaining therein.
589 if (set
->blocks
!= NULL
)
591 block
->next
= set
->blocks
->next
;
592 set
->blocks
->next
= block
;
600 set
->isReset
= false;
602 AllocAllocInfo(set
, chunk
);
603 return AllocChunkGetPointer(chunk
);
607 * Request is small enough to be treated as a chunk. Look in the
608 * corresponding free list to see if there is a free chunk we could reuse.
609 * If one is found, remove it from the free list, make it again a member
610 * of the alloc set and return its data address.
612 fidx
= AllocSetFreeIndex(size
);
613 chunk
= set
->freelist
[fidx
];
616 Assert(chunk
->size
>= size
);
618 set
->freelist
[fidx
] = (AllocChunk
) chunk
->aset
;
620 chunk
->aset
= (void *) set
;
622 #ifdef MEMORY_CONTEXT_CHECKING
623 chunk
->requested_size
= size
;
624 /* set mark to catch clobber of "unused" space */
625 if (size
< chunk
->size
)
626 ((char *) AllocChunkGetPointer(chunk
))[size
] = 0x7E;
628 #ifdef RANDOMIZE_ALLOCATED_MEMORY
629 /* fill the allocated space with junk */
630 randomize_mem((char *) AllocChunkGetPointer(chunk
), size
);
633 /* isReset must be false already */
634 Assert(!set
->isReset
);
636 AllocAllocInfo(set
, chunk
);
637 return AllocChunkGetPointer(chunk
);
641 * Choose the actual chunk size to allocate.
643 chunk_size
= (1 << ALLOC_MINBITS
) << fidx
;
644 Assert(chunk_size
>= size
);
647 * If there is enough room in the active allocation block, we will put the
648 * chunk into that block. Else must start a new one.
650 if ((block
= set
->blocks
) != NULL
)
652 Size availspace
= block
->endptr
- block
->freeptr
;
654 if (availspace
< (chunk_size
+ ALLOC_CHUNKHDRSZ
))
657 * The existing active (top) block does not have enough room for
658 * the requested allocation, but it might still have a useful
659 * amount of space in it. Once we push it down in the block list,
660 * we'll never try to allocate more space from it. So, before we
661 * do that, carve up its free space into chunks that we can put on
662 * the set's freelists.
664 * Because we can only get here when there's less than
665 * ALLOC_CHUNK_LIMIT left in the block, this loop cannot iterate
666 * more than ALLOCSET_NUM_FREELISTS-1 times.
668 while (availspace
>= ((1 << ALLOC_MINBITS
) + ALLOC_CHUNKHDRSZ
))
670 Size availchunk
= availspace
- ALLOC_CHUNKHDRSZ
;
671 int a_fidx
= AllocSetFreeIndex(availchunk
);
674 * In most cases, we'll get back the index of the next larger
675 * freelist than the one we need to put this chunk on. The
676 * exception is when availchunk is exactly a power of 2.
678 if (availchunk
!= (1 << (a_fidx
+ ALLOC_MINBITS
)))
682 availchunk
= (1 << (a_fidx
+ ALLOC_MINBITS
));
685 chunk
= (AllocChunk
) (block
->freeptr
);
687 block
->freeptr
+= (availchunk
+ ALLOC_CHUNKHDRSZ
);
688 availspace
-= (availchunk
+ ALLOC_CHUNKHDRSZ
);
690 chunk
->size
= availchunk
;
691 #ifdef MEMORY_CONTEXT_CHECKING
692 chunk
->requested_size
= 0; /* mark it free */
694 chunk
->aset
= (void *) set
->freelist
[a_fidx
];
695 set
->freelist
[a_fidx
] = chunk
;
698 /* Mark that we need to create a new block */
704 * Time to create a new regular (multi-chunk) block?
711 * The first such block has size initBlockSize, and we double the
712 * space in each succeeding block, but not more than maxBlockSize.
714 blksize
= set
->nextBlockSize
;
715 set
->nextBlockSize
<<= 1;
716 if (set
->nextBlockSize
> set
->maxBlockSize
)
717 set
->nextBlockSize
= set
->maxBlockSize
;
720 * If initBlockSize is less than ALLOC_CHUNK_LIMIT, we could need more
721 * space... but try to keep it a power of 2.
723 required_size
= chunk_size
+ ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
;
724 while (blksize
< required_size
)
727 /* Try to allocate it */
728 block
= (AllocBlock
) malloc(blksize
);
731 * We could be asking for pretty big blocks here, so cope if malloc
732 * fails. But give up if there's less than a meg or so available...
734 while (block
== NULL
&& blksize
> 1024 * 1024)
737 if (blksize
< required_size
)
739 block
= (AllocBlock
) malloc(blksize
);
744 MemoryContextStats(TopMemoryContext
);
746 (errcode(ERRCODE_OUT_OF_MEMORY
),
747 errmsg("out of memory"),
748 errdetail("Failed on request of size %lu.",
749 (unsigned long) size
)));
753 block
->freeptr
= ((char *) block
) + ALLOC_BLOCKHDRSZ
;
754 block
->endptr
= ((char *) block
) + blksize
;
757 * If this is the first block of the set, make it the "keeper" block.
758 * Formerly, a keeper block could only be created during context
759 * creation, but allowing it to happen here lets us have fast reset
760 * cycling even for contexts created with minContextSize = 0; that way
761 * we don't have to force space to be allocated in contexts that might
762 * never need any space. Don't mark an oversize block as a keeper,
765 if (set
->keeper
== NULL
&& blksize
== set
->initBlockSize
)
768 block
->next
= set
->blocks
;
773 * OK, do the allocation
775 chunk
= (AllocChunk
) (block
->freeptr
);
777 block
->freeptr
+= (chunk_size
+ ALLOC_CHUNKHDRSZ
);
778 Assert(block
->freeptr
<= block
->endptr
);
780 chunk
->aset
= (void *) set
;
781 chunk
->size
= chunk_size
;
782 #ifdef MEMORY_CONTEXT_CHECKING
783 chunk
->requested_size
= size
;
784 /* set mark to catch clobber of "unused" space */
785 if (size
< chunk
->size
)
786 ((char *) AllocChunkGetPointer(chunk
))[size
] = 0x7E;
788 #ifdef RANDOMIZE_ALLOCATED_MEMORY
789 /* fill the allocated space with junk */
790 randomize_mem((char *) AllocChunkGetPointer(chunk
), size
);
793 set
->isReset
= false;
795 AllocAllocInfo(set
, chunk
);
796 return AllocChunkGetPointer(chunk
);
801 * Frees allocated memory; memory is removed from the set.
804 AllocSetFree(MemoryContext context
, void *pointer
)
806 AllocSet set
= (AllocSet
) context
;
807 AllocChunk chunk
= AllocPointerGetChunk(pointer
);
809 AllocFreeInfo(set
, chunk
);
811 #ifdef MEMORY_CONTEXT_CHECKING
812 /* Test for someone scribbling on unused space in chunk */
813 if (chunk
->requested_size
< chunk
->size
)
814 if (((char *) pointer
)[chunk
->requested_size
] != 0x7E)
815 elog(WARNING
, "detected write past chunk end in %s %p",
816 set
->header
.name
, chunk
);
819 if (chunk
->size
> set
->allocChunkLimit
)
822 * Big chunks are certain to have been allocated as single-chunk
823 * blocks. Find the containing block and return it to malloc().
825 AllocBlock block
= set
->blocks
;
826 AllocBlock prevblock
= NULL
;
828 while (block
!= NULL
)
830 if (chunk
== (AllocChunk
) (((char *) block
) + ALLOC_BLOCKHDRSZ
))
836 elog(ERROR
, "could not find block containing chunk %p", chunk
);
837 /* let's just make sure chunk is the only one in the block */
838 Assert(block
->freeptr
== ((char *) block
) +
839 (chunk
->size
+ ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
));
841 /* OK, remove block from aset's list and free it */
842 if (prevblock
== NULL
)
843 set
->blocks
= block
->next
;
845 prevblock
->next
= block
->next
;
846 #ifdef CLOBBER_FREED_MEMORY
847 /* Wipe freed memory for debugging purposes */
848 memset(block
, 0x7F, block
->freeptr
- ((char *) block
));
854 /* Normal case, put the chunk into appropriate freelist */
855 int fidx
= AllocSetFreeIndex(chunk
->size
);
857 chunk
->aset
= (void *) set
->freelist
[fidx
];
859 #ifdef CLOBBER_FREED_MEMORY
860 /* Wipe freed memory for debugging purposes */
861 memset(pointer
, 0x7F, chunk
->size
);
864 #ifdef MEMORY_CONTEXT_CHECKING
865 /* Reset requested_size to 0 in chunks that are on freelist */
866 chunk
->requested_size
= 0;
868 set
->freelist
[fidx
] = chunk
;
874 * Returns new pointer to allocated memory of given size; this memory
875 * is added to the set. Memory associated with given pointer is copied
876 * into the new memory, and the old memory is freed.
879 AllocSetRealloc(MemoryContext context
, void *pointer
, Size size
)
881 AllocSet set
= (AllocSet
) context
;
882 AllocChunk chunk
= AllocPointerGetChunk(pointer
);
883 Size oldsize
= chunk
->size
;
885 #ifdef MEMORY_CONTEXT_CHECKING
886 /* Test for someone scribbling on unused space in chunk */
887 if (chunk
->requested_size
< oldsize
)
888 if (((char *) pointer
)[chunk
->requested_size
] != 0x7E)
889 elog(WARNING
, "detected write past chunk end in %s %p",
890 set
->header
.name
, chunk
);
893 /* isReset must be false already */
894 Assert(!set
->isReset
);
897 * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
898 * allocated area already is >= the new size. (In particular, we always
899 * fall out here if the requested size is a decrease.)
903 #ifdef MEMORY_CONTEXT_CHECKING
904 #ifdef RANDOMIZE_ALLOCATED_MEMORY
905 /* We can only fill the extra space if we know the prior request */
906 if (size
> chunk
->requested_size
)
907 randomize_mem((char *) AllocChunkGetPointer(chunk
) + chunk
->requested_size
,
908 size
- chunk
->requested_size
);
911 chunk
->requested_size
= size
;
912 /* set mark to catch clobber of "unused" space */
914 ((char *) pointer
)[size
] = 0x7E;
919 if (oldsize
> set
->allocChunkLimit
)
922 * The chunk must have been allocated as a single-chunk block. Find
923 * the containing block and use realloc() to make it bigger with
924 * minimum space wastage.
926 AllocBlock block
= set
->blocks
;
927 AllocBlock prevblock
= NULL
;
931 while (block
!= NULL
)
933 if (chunk
== (AllocChunk
) (((char *) block
) + ALLOC_BLOCKHDRSZ
))
939 elog(ERROR
, "could not find block containing chunk %p", chunk
);
940 /* let's just make sure chunk is the only one in the block */
941 Assert(block
->freeptr
== ((char *) block
) +
942 (chunk
->size
+ ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
));
945 chksize
= MAXALIGN(size
);
946 blksize
= chksize
+ ALLOC_BLOCKHDRSZ
+ ALLOC_CHUNKHDRSZ
;
947 block
= (AllocBlock
) realloc(block
, blksize
);
950 MemoryContextStats(TopMemoryContext
);
952 (errcode(ERRCODE_OUT_OF_MEMORY
),
953 errmsg("out of memory"),
954 errdetail("Failed on request of size %lu.",
955 (unsigned long) size
)));
957 block
->freeptr
= block
->endptr
= ((char *) block
) + blksize
;
959 /* Update pointers since block has likely been moved */
960 chunk
= (AllocChunk
) (((char *) block
) + ALLOC_BLOCKHDRSZ
);
961 if (prevblock
== NULL
)
964 prevblock
->next
= block
;
965 chunk
->size
= chksize
;
967 #ifdef MEMORY_CONTEXT_CHECKING
968 #ifdef RANDOMIZE_ALLOCATED_MEMORY
969 /* We can only fill the extra space if we know the prior request */
970 randomize_mem((char *) AllocChunkGetPointer(chunk
) + chunk
->requested_size
,
971 size
- chunk
->requested_size
);
974 chunk
->requested_size
= size
;
975 /* set mark to catch clobber of "unused" space */
976 if (size
< chunk
->size
)
977 ((char *) AllocChunkGetPointer(chunk
))[size
] = 0x7E;
980 return AllocChunkGetPointer(chunk
);
985 * Small-chunk case. We just do this by brute force, ie, allocate a
986 * new chunk and copy the data. Since we know the existing data isn't
987 * huge, this won't involve any great memcpy expense, so it's not
988 * worth being smarter. (At one time we tried to avoid memcpy when it
989 * was possible to enlarge the chunk in-place, but that turns out to
990 * misbehave unpleasantly for repeated cycles of
991 * palloc/repalloc/pfree: the eventually freed chunks go into the
992 * wrong freelist for the next initial palloc request, and so we leak
993 * memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
995 AllocPointer newPointer
;
997 /* allocate new chunk */
998 newPointer
= AllocSetAlloc((MemoryContext
) set
, size
);
1000 /* transfer existing data (certain to fit) */
1001 memcpy(newPointer
, pointer
, oldsize
);
1003 /* free old chunk */
1004 AllocSetFree((MemoryContext
) set
, pointer
);
1011 * AllocSetGetChunkSpace
1012 * Given a currently-allocated chunk, determine the total space
1013 * it occupies (including all memory-allocation overhead).
1016 AllocSetGetChunkSpace(MemoryContext context
, void *pointer
)
1018 AllocChunk chunk
= AllocPointerGetChunk(pointer
);
1020 return chunk
->size
+ ALLOC_CHUNKHDRSZ
;
1025 * Is an allocset empty of any allocated space?
1028 AllocSetIsEmpty(MemoryContext context
)
1030 AllocSet set
= (AllocSet
) context
;
1033 * For now, we say "empty" only if the context is new or just reset. We
1034 * could examine the freelists to determine if all space has been freed,
1035 * but it's not really worth the trouble for present uses of this
1045 * Displays stats about memory consumption of an allocset.
1048 AllocSetStats(MemoryContext context
, int level
)
1050 AllocSet set
= (AllocSet
) context
;
1053 long totalspace
= 0;
1060 for (block
= set
->blocks
; block
!= NULL
; block
= block
->next
)
1063 totalspace
+= block
->endptr
- ((char *) block
);
1064 freespace
+= block
->endptr
- block
->freeptr
;
1066 for (fidx
= 0; fidx
< ALLOCSET_NUM_FREELISTS
; fidx
++)
1068 for (chunk
= set
->freelist
[fidx
]; chunk
!= NULL
;
1069 chunk
= (AllocChunk
) chunk
->aset
)
1072 freespace
+= chunk
->size
+ ALLOC_CHUNKHDRSZ
;
1076 for (i
= 0; i
< level
; i
++)
1077 fprintf(stderr
, " ");
1080 "%s: %lu total in %ld blocks; %lu free (%ld chunks); %lu used\n",
1081 set
->header
.name
, totalspace
, nblocks
, freespace
, nchunks
,
1082 totalspace
- freespace
);
1086 #ifdef MEMORY_CONTEXT_CHECKING
1090 * Walk through chunks and check consistency of memory.
1092 * NOTE: report errors as WARNING, *not* ERROR or FATAL. Otherwise you'll
1093 * find yourself in an infinite loop when trouble occurs, because this
1094 * routine will be entered again when elog cleanup tries to release memory!
1097 AllocSetCheck(MemoryContext context
)
1099 AllocSet set
= (AllocSet
) context
;
1100 char *name
= set
->header
.name
;
1103 for (block
= set
->blocks
; block
!= NULL
; block
= block
->next
)
1105 char *bpoz
= ((char *) block
) + ALLOC_BLOCKHDRSZ
;
1106 long blk_used
= block
->freeptr
- bpoz
;
1111 * Empty block - empty can be keeper-block only
1115 if (set
->keeper
!= block
)
1116 elog(WARNING
, "problem in alloc set %s: empty block %p",
1123 while (bpoz
< block
->freeptr
)
1125 AllocChunk chunk
= (AllocChunk
) bpoz
;
1130 chsize
= chunk
->size
; /* aligned chunk size */
1131 dsize
= chunk
->requested_size
; /* real data */
1132 chdata_end
= ((char *) chunk
) + (ALLOC_CHUNKHDRSZ
+ dsize
);
1138 elog(WARNING
, "problem in alloc set %s: req size > alloc size for chunk %p in block %p",
1139 name
, chunk
, block
);
1140 if (chsize
< (1 << ALLOC_MINBITS
))
1141 elog(WARNING
, "problem in alloc set %s: bad size %lu for chunk %p in block %p",
1142 name
, (unsigned long) chsize
, chunk
, block
);
1144 /* single-chunk block? */
1145 if (chsize
> set
->allocChunkLimit
&&
1146 chsize
+ ALLOC_CHUNKHDRSZ
!= blk_used
)
1147 elog(WARNING
, "problem in alloc set %s: bad single-chunk %p in block %p",
1148 name
, chunk
, block
);
1151 * If chunk is allocated, check for correct aset pointer. (If it's
1152 * free, the aset is the freelist pointer, which we can't check as
1155 if (dsize
> 0 && chunk
->aset
!= (void *) set
)
1156 elog(WARNING
, "problem in alloc set %s: bogus aset link in block %p, chunk %p",
1157 name
, block
, chunk
);
1160 * Check for overwrite of "unallocated" space in chunk
1162 if (dsize
> 0 && dsize
< chsize
&& *chdata_end
!= 0x7E)
1163 elog(WARNING
, "problem in alloc set %s: detected write past chunk end in block %p, chunk %p",
1164 name
, block
, chunk
);
1169 bpoz
+= ALLOC_CHUNKHDRSZ
+ chsize
;
1172 if ((blk_data
+ (nchunks
* ALLOC_CHUNKHDRSZ
)) != blk_used
)
1173 elog(WARNING
, "problem in alloc set %s: found inconsistent memory block %p",
1178 #endif /* MEMORY_CONTEXT_CHECKING */