// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // General C malloc/free, but intended for Go. // Same design as tcmalloc: // see https://www/eng/designdocs/tcmalloc/tcmalloc.html // TODO: // * Central free lists. // * Thread cache stealing. // * Return memory to the OS. // * Memory footprint during testrandom is too big. // * Need to coalesce adjacent free spans. // // *** Some way to avoid the ``malloc overflows the stack // during the stack overflow malloc'' problem. #include "malloc.h" Central central; PageMap spanmap; // Insert a new span into the map. static void insertspan(Span *s) { int32 i; uintptr base; // TODO: This is likely too slow for large spans. base = (uintptr)s->base >> PageShift; for(i=0; ilength; i++) pminsert(&spanmap, base+i, s); } // Record that a span has gotten smaller. static void shrinkspan(Span *s, int32 newlength) { int32 i; uintptr base; // TODO: This is unnecessary, because an insertspan is next. base = (uintptr)s->base >> PageShift; for(i=newlength; ilength; i++) pminsert(&spanmap, base+i, nil); s->length = newlength; } // Find the span for a given pointer. static Span* spanofptr(void *v) { return pmlookup(&spanmap, (uintptr)v >> PageShift); } static void freespan(Span*); // Linked list of spans. // TODO(rsc): Remove - should be able to walk pagemap. Span *spanfirst; Span *spanlast; static void addtolist(Span *s) { if(spanlast) { s->aprev = spanlast; s->aprev->anext = s; } else { s->aprev = nil; spanfirst = s; } s->anext = nil; spanlast = s; } /* static void delfromlist(Span *s) { if(s->aprev) s->aprev->anext = s->anext; else spanfirst = s->anext; if(s->anext) s->anext->aprev = s->aprev; else spanlast = s->aprev; } */ // Allocate a span of at least n pages. static Span* allocspan(int32 npage) { Span *s, **l, *s1; int32 allocnpage, i; // Look in the n-page free lists for big enough n. for(i=npage; inext; goto havespan; } } // Look in the large list, which has large runs of pages. for(l=¢ral.large; (s=*l) != nil; l=&s->next) { if(s->length >= npage) { *l = s->next; s->next = nil; //if(s->length > npage) printf("Chop span %D for %d\n", s->length, npage); goto havespan; } } // Otherwise we need more memory. // TODO: Could try to release this lock while asking for memory. s = trivalloc(sizeof *s); allocnpage = npage; if(allocnpage < (1<<20>>PageShift)) // TODO: Tune allocnpage = (1<<20>>PageShift); s->length = allocnpage; //printf("New span %d for %d\n", allocnpage, npage); s->base = trivalloc(allocnpage<length > npage) { s1 = trivalloc(sizeof *s); s1->base = s->base + (npage << PageShift); s1->length = s->length - npage; shrinkspan(s, npage); insertspan(s1); addtolist(s1); freespan(s1); } s->state = SpanInUse; return s; } // Free a span. // TODO(rsc): Coalesce adjacent free spans. static void freespan(Span *s) { Span **l; Span *ss; s->state = SpanFree; if(s->length < nelem(central.free)) { s->next = central.free[s->length]; central.free[s->length] = s; } else { // Keep central.large sorted in // increasing size for best-fit allocation. for(l = ¢ral.large; (ss=*l) != nil; l=&ss->next) if(ss->length >= s->length) break; s->next = *l; *l = s; } } // Small objects are kept on per-size free lists in the M. // There are SmallFreeClasses (defined in runtime.h) different lists. int32 classtosize[SmallFreeClasses] = { /* seq 8 8 127 | sed 's/$/,/' | fmt seq 128 16 255 | sed 's/$/,/' | fmt seq 256 32 511 | sed 's/$/,/' | fmt seq 512 64 1023 | sed 's/$/,/' | fmt seq 1024 128 2047 | sed 's/$/,/' | fmt seq 2048 256 32768 | sed 's/$/,/' | fmt */ 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128, 144, 160, 176, 192, 208, 224, 240, 256, 288, 320, 352, 384, 416, 448, 480, 512, 576, 640, 704, 768, 832, 896, 960, 1024, 1152, 1280, 1408, 1536, 1664, 1792, 1920, 2048, 2304, 2560, 2816, 3072, 3328, 3584, 3840, 4096, 4352, 4608, 4864, 5120, 5376, 5632, 5888, 6144, 6400, 6656, 6912, 7168, 7424, 7680, 7936, 8192, 8448, 8704, 8960, 9216, 9472, 9728, 9984, 10240, 10496, 10752, 11008, 11264, 11520, 11776, 12032, 12288, 12544, 12800, 13056, 13312, 13568, 13824, 14080, 14336, 14592, 14848, 15104, 15360, 15616, 15872, 16128, 16384, 16640, 16896, 17152, 17408, 17664, 17920, 18176, 18432, 18688, 18944, 19200, 19456, 19712, 19968, 20224, 20480, 20736, 20992, 21248, 21504, 21760, 22016, 22272, 22528, 22784, 23040, 23296, 23552, 23808, 24064, 24320, 24576, 24832, 25088, 25344, 25600, 25856, 26112, 26368, 26624, 26880, 27136, 27392, 27648, 27904, 28160, 28416, 28672, 28928, 29184, 29440, 29696, 29952, 30208, 30464, 30720, 30976, 31232, 31488, 31744, 32000, 32256, 32512, 32768, }; enum { LargeSize = 32768 }; // Trigger compile error if nelem(classtosize) != SmallFreeClasses. static int32 zzz1[SmallFreeClasses-nelem(classtosize)+1]; static int32 zzz2[nelem(classtosize)-SmallFreeClasses+1]; static int32 sizetoclass(int32 siz) { if(siz <= 0) return 0; if(siz <= 128) return (siz-1) >> 3; if(siz <= 256) return ((siz-1) >> 4) + 8; if(siz <= 512) return ((siz-1) >> 5) + 16; if(siz <= 1024) return ((siz-1) >> 6) + 24; if(siz <= 2048) return ((siz-1) >> 7) + 32; if(siz <= 32768) return ((siz-1) >> 8) + 40; throw("sizetoclass - invalid size"); return -1; } void allocator·testsizetoclass(void) { int32 i, n; n = 0; for(i=0; i 1<<20) { chunk = 1<<20; } chunk = (chunk+PageMask) & ~PageMask; s = allocspan(chunk>>PageShift); //printf("New class %d\n", cl); s->state = SpanInUse; s->cl = cl; siz = classtosize[cl]; n = chunk/(siz+sizeof(s->refbase[0])); p = s->base; //printf("centralgrab cl=%d siz=%d n=%d\n", cl, siz, n); for(i=0; irefbase = (int32*)p; // TODO(rsc): Remove - only for mark/sweep for(i=0; irefbase[i] = RefFree; *pn = n; return s->base; } // Allocate a small object of size class cl. void* allocsmall(int32 cl) { void **p; int32 n; if(cl < 0 || cl >= SmallFreeClasses) throw("allocsmall - invalid class"); // try m-local cache. p = m->freelist[cl]; if(p == nil) { // otherwise grab some blocks from central cache. lock(¢ral); //printf("centralgrab for %d\n", cl); p = centralgrab(cl, &n); // TODO: update local counters using n unlock(¢ral); } //printf("alloc from cl %d %p\n", cl, p); // advance linked list. m->freelist[cl] = *p; // TODO(rsc): If cl > 0, can store ref ptr in *(p+1), // avoiding call to findobj. // Or could get rid of RefFree, which is only truly // necessary for mark/sweep. int32 *ref; if(!findobj(p, nil, nil, &ref)) throw("bad findobj"); if(*ref != RefFree) throw("double alloc"); *ref = 0; // Blocks on free list are zeroed except for // the linked list pointer that we just used. Zero it. *p = 0; return p; } // Allocate large object of np pages. void* alloclarge(int32 np) { Span *s; lock(¢ral); //printf("Alloc span %d\n", np); s = allocspan(np); unlock(¢ral); s->state = SpanInUse; s->cl = -1; s->ref = 0; return s->base; } // Allocate object of n bytes. void* alloc(int32 n) { int32 cl, np; if(n < LargeSize) { cl = sizetoclass(n); if(cl < 0 || cl >= SmallFreeClasses) { printf("%d -> %d\n", n, cl); throw("alloc - logic error"); } allocator·allocated += classtosize[cl]; return allocsmall(cl); } // count number of pages; careful about overflow for big n. np = (n>>PageShift) + (((n&PageMask)+PageMask)>>PageShift); allocator·allocated += (uint64)np<state != SpanInUse) return false; // Big object if(s->cl < 0) { if(obj) *obj = s->base; if(size) *size = s->length<ref; return true; } // Small object if((byte*)v >= (byte*)s->refbase) return false; siz = classtosize[s->cl]; off = (byte*)v - (byte*)s->base; indx = off/siz; if(obj) *obj = s->base + indx*siz; if(size) *size = siz; if(ref) *ref = s->refbase + indx; return true; } void allocator·find(uint64 ptr, byte *obj, int64 siz, int32 *ref, bool ok) { ok = findobj((void*)ptr, &obj, &siz, &ref); FLUSH(&ok); } // Free object with base pointer v. void free(void *v) { void **p; Span *s; int32 siz, off, n; s = spanofptr(v); if(s->state != SpanInUse) throw("free - invalid pointer1"); // Big object should be s->base. if(s->cl < 0) { if(v != s->base) throw("free - invalid pointer2"); // TODO: For large spans, maybe just return the // memory to the operating system and let it zero it. if(s->ref != 0 && s->ref != RefManual && s->ref != RefStack) throw("free - bad ref count"); s->ref = RefFree; sys·memclr(s->base, s->length << PageShift); //printf("Free big %D\n", s->length); allocator·allocated -= s->length << PageShift; lock(¢ral); freespan(s); unlock(¢ral); return; } // Small object should be aligned properly. if((byte*)v >= (byte*)s->refbase) throw("free - invalid pointer4"); siz = classtosize[s->cl]; off = (byte*)v - (byte*)s->base; if(off%siz) throw("free - invalid pointer3"); n = off/siz; if(s->refbase[n] != 0 && s->refbase[n] != RefManual && s->refbase[n] != RefStack) throw("free - bad ref count1"); s->refbase[n] = RefFree; // Zero and add to free list. sys·memclr(v, siz); allocator·allocated -= siz; p = v; *p = m->freelist[s->cl]; m->freelist[s->cl] = p; //printf("Free siz %d cl %d\n", siz, s->cl); } void allocator·free(byte *v) { free(v); } void allocator·memset(byte *v, int32 c, int32 n) { int32 i; for(i=0; i