mirror of
https://github.com/golang/go
synced 2024-11-26 16:26:49 -07:00
3f8aa662e9
mark and sweep, stop the world garbage collector (intermediate step in the way to ref counting). can run pretty with an explicit gc after each file. R=r DELTA=502 (346 added, 143 deleted, 13 changed) OCL=20630 CL=20635
494 lines
11 KiB
C
494 lines
11 KiB
C
// 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; i<s->length; 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; i<s->length; 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; i<nelem(central.free); i++) {
|
|
s = central.free[i];
|
|
if(s != nil) {
|
|
central.free[i] = s->next;
|
|
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<<PageShift);
|
|
insertspan(s);
|
|
addtolist(s);
|
|
|
|
havespan:
|
|
// If span is bigger than needed, redistribute the remainder.
|
|
if(s->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<nelem(classtosize); i++) {
|
|
for(; n <= classtosize[i]; n++) {
|
|
if(sizetoclass(n) != i) {
|
|
printf("sizetoclass %d = %d want %d\n", n, sizetoclass(n), i);
|
|
throw("testsizetoclass");
|
|
}
|
|
}
|
|
}
|
|
if (n != 32768+1) {
|
|
printf("testsizetoclass stopped at %d\n", n);
|
|
throw("testsizetoclass");
|
|
}
|
|
}
|
|
|
|
// Grab a bunch of objects of size class cl off the central free list.
|
|
// Set *pn to the number of objects returned.
|
|
static void*
|
|
centralgrab(int32 cl, int32 *pn)
|
|
{
|
|
byte *p;
|
|
Span *s;
|
|
int32 chunk, i, n, siz;
|
|
|
|
// For now there is no central free list.
|
|
// Fall back to allocating a new span
|
|
// and chopping it up.
|
|
chunk = classtosize[cl] * 1024;
|
|
if(chunk > 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; i<n; i++) {
|
|
if(i < n-1)
|
|
*(void**)p = p+siz;
|
|
p += siz;
|
|
}
|
|
s->refbase = (int32*)p;
|
|
|
|
// TODO(rsc): Remove - only for mark/sweep
|
|
for(i=0; i<n; i++)
|
|
s->refbase[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<<PageShift;
|
|
return alloclarge(np);
|
|
}
|
|
|
|
void
|
|
allocator·malloc(int32 n, byte *out)
|
|
{
|
|
out = alloc(n);
|
|
FLUSH(&out);
|
|
}
|
|
|
|
// Check whether v points into a known memory block.
|
|
// If so, return true with
|
|
// *obj = base pointer of object (can pass to free)
|
|
// *size = size of object
|
|
// *ref = pointer to ref count for object
|
|
// Object might already be freed, in which case *ref == RefFree.
|
|
bool
|
|
findobj(void *v, void **obj, int64 *size, int32 **ref)
|
|
{
|
|
Span *s;
|
|
int32 siz, off, indx;
|
|
|
|
s = spanofptr(v);
|
|
if(s == nil || s->state != SpanInUse)
|
|
return false;
|
|
|
|
// Big object
|
|
if(s->cl < 0) {
|
|
if(obj)
|
|
*obj = s->base;
|
|
if(size)
|
|
*size = s->length<<PageShift;
|
|
if(ref)
|
|
*ref = &s->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<n; i++)
|
|
v[i] = c;
|
|
}
|
|
|