diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/pkg/runtime/malloc.goc | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-upstream/60.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/pkg/runtime/malloc.goc')
-rw-r--r-- | src/pkg/runtime/malloc.goc | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/src/pkg/runtime/malloc.goc b/src/pkg/runtime/malloc.goc new file mode 100644 index 000000000..b9fe36db6 --- /dev/null +++ b/src/pkg/runtime/malloc.goc @@ -0,0 +1,482 @@ +// 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. + +// See malloc.h for overview. +// +// TODO(rsc): double-check stats. + +package runtime +#include "runtime.h" +#include "stack.h" +#include "malloc.h" +#include "defs.h" +#include "type.h" + +MHeap runtime·mheap; +extern MStats mstats; // defined in extern.go + +extern volatile int32 runtime·MemProfileRate; + +// Allocate an object of at least size bytes. +// Small objects are allocated from the per-thread cache's free lists. +// Large objects (> 32 kB) are allocated straight from the heap. +void* +runtime·mallocgc(uintptr size, uint32 flag, int32 dogc, int32 zeroed) +{ + int32 sizeclass, rate; + MCache *c; + uintptr npages; + MSpan *s; + void *v; + + if(runtime·gcwaiting && g != m->g0 && m->locks == 0) + runtime·gosched(); + if(m->mallocing) + runtime·throw("malloc/free - deadlock"); + m->mallocing = 1; + if(size == 0) + size = 1; + + c = m->mcache; + c->local_nmalloc++; + if(size <= MaxSmallSize) { + // Allocate from mcache free lists. + sizeclass = runtime·SizeToClass(size); + size = runtime·class_to_size[sizeclass]; + v = runtime·MCache_Alloc(c, sizeclass, size, zeroed); + if(v == nil) + runtime·throw("out of memory"); + c->local_alloc += size; + c->local_total_alloc += size; + c->local_by_size[sizeclass].nmalloc++; + } else { + // TODO(rsc): Report tracebacks for very large allocations. + + // Allocate directly from heap. + npages = size >> PageShift; + if((size & PageMask) != 0) + npages++; + s = runtime·MHeap_Alloc(&runtime·mheap, npages, 0, 1); + if(s == nil) + runtime·throw("out of memory"); + size = npages<<PageShift; + c->local_alloc += size; + c->local_total_alloc += size; + v = (void*)(s->start << PageShift); + + // setup for mark sweep + runtime·markspan(v, 0, 0, true); + } + if(!(flag & FlagNoGC)) + runtime·markallocated(v, size, (flag&FlagNoPointers) != 0); + + m->mallocing = 0; + + if(!(flag & FlagNoProfiling) && (rate = runtime·MemProfileRate) > 0) { + if(size >= rate) + goto profile; + if(m->mcache->next_sample > size) + m->mcache->next_sample -= size; + else { + // pick next profile time + if(rate > 0x3fffffff) // make 2*rate not overflow + rate = 0x3fffffff; + m->mcache->next_sample = runtime·fastrand1() % (2*rate); + profile: + runtime·setblockspecial(v); + runtime·MProf_Malloc(v, size); + } + } + + if(dogc && mstats.heap_alloc >= mstats.next_gc) + runtime·gc(0); + return v; +} + +void* +runtime·malloc(uintptr size) +{ + return runtime·mallocgc(size, 0, 0, 1); +} + +// Free the object whose base pointer is v. +void +runtime·free(void *v) +{ + int32 sizeclass; + MSpan *s; + MCache *c; + uint32 prof; + uintptr size; + + if(v == nil) + return; + + // If you change this also change mgc0.c:/^sweepspan, + // which has a copy of the guts of free. + + if(m->mallocing) + runtime·throw("malloc/free - deadlock"); + m->mallocing = 1; + + if(!runtime·mlookup(v, nil, nil, &s)) { + runtime·printf("free %p: not an allocated block\n", v); + runtime·throw("free runtime·mlookup"); + } + prof = runtime·blockspecial(v); + + // Find size class for v. + sizeclass = s->sizeclass; + c = m->mcache; + if(sizeclass == 0) { + // Large object. + size = s->npages<<PageShift; + *(uintptr*)(s->start<<PageShift) = 1; // mark as "needs to be zeroed" + // Must mark v freed before calling unmarkspan and MHeap_Free: + // they might coalesce v into other spans and change the bitmap further. + runtime·markfreed(v, size); + runtime·unmarkspan(v, 1<<PageShift); + runtime·MHeap_Free(&runtime·mheap, s, 1); + } else { + // Small object. + size = runtime·class_to_size[sizeclass]; + if(size > sizeof(uintptr)) + ((uintptr*)v)[1] = 1; // mark as "needs to be zeroed" + // Must mark v freed before calling MCache_Free: + // it might coalesce v and other blocks into a bigger span + // and change the bitmap further. + runtime·markfreed(v, size); + c->local_by_size[sizeclass].nfree++; + runtime·MCache_Free(c, v, sizeclass, size); + } + c->local_alloc -= size; + if(prof) + runtime·MProf_Free(v, size); + m->mallocing = 0; +} + +int32 +runtime·mlookup(void *v, byte **base, uintptr *size, MSpan **sp) +{ + uintptr n, i; + byte *p; + MSpan *s; + + m->mcache->local_nlookup++; + s = runtime·MHeap_LookupMaybe(&runtime·mheap, v); + if(sp) + *sp = s; + if(s == nil) { + runtime·checkfreed(v, 1); + if(base) + *base = nil; + if(size) + *size = 0; + return 0; + } + + p = (byte*)((uintptr)s->start<<PageShift); + if(s->sizeclass == 0) { + // Large object. + if(base) + *base = p; + if(size) + *size = s->npages<<PageShift; + return 1; + } + + if((byte*)v >= (byte*)s->limit) { + // pointers past the last block do not count as pointers. + return 0; + } + + n = runtime·class_to_size[s->sizeclass]; + if(base) { + i = ((byte*)v - p)/n; + *base = p + i*n; + } + if(size) + *size = n; + + return 1; +} + +MCache* +runtime·allocmcache(void) +{ + MCache *c; + + runtime·lock(&runtime·mheap); + c = runtime·FixAlloc_Alloc(&runtime·mheap.cachealloc); + mstats.mcache_inuse = runtime·mheap.cachealloc.inuse; + mstats.mcache_sys = runtime·mheap.cachealloc.sys; + runtime·unlock(&runtime·mheap); + return c; +} + +void +runtime·purgecachedstats(M* m) +{ + MCache *c; + + // Protected by either heap or GC lock. + c = m->mcache; + mstats.heap_alloc += c->local_cachealloc; + c->local_cachealloc = 0; + mstats.heap_objects += c->local_objects; + c->local_objects = 0; + mstats.nmalloc += c->local_nmalloc; + c->local_nmalloc = 0; + mstats.nfree += c->local_nfree; + c->local_nfree = 0; + mstats.nlookup += c->local_nlookup; + c->local_nlookup = 0; + mstats.alloc += c->local_alloc; + c->local_alloc= 0; + mstats.total_alloc += c->local_total_alloc; + c->local_total_alloc= 0; +} + +uintptr runtime·sizeof_C_MStats = sizeof(MStats); + +#define MaxArena32 (2U<<30) + +void +runtime·mallocinit(void) +{ + byte *p; + uintptr arena_size, bitmap_size; + extern byte end[]; + + runtime·InitSizes(); + + // Set up the allocation arena, a contiguous area of memory where + // allocated data will be found. The arena begins with a bitmap large + // enough to hold 4 bits per allocated word. + if(sizeof(void*) == 8) { + // On a 64-bit machine, allocate from a single contiguous reservation. + // 16 GB should be big enough for now. + // + // The code will work with the reservation at any address, but ask + // SysReserve to use 0x000000f800000000 if possible. + // Allocating a 16 GB region takes away 36 bits, and the amd64 + // doesn't let us choose the top 17 bits, so that leaves the 11 bits + // in the middle of 0x00f8 for us to choose. Choosing 0x00f8 means + // that the valid memory addresses will begin 0x00f8, 0x00f9, 0x00fa, 0x00fb. + // None of the bytes f8 f9 fa fb can appear in valid UTF-8, and + // they are otherwise as far from ff (likely a common byte) as possible. + // Choosing 0x00 for the leading 6 bits was more arbitrary, but it + // is not a common ASCII code point either. Using 0x11f8 instead + // caused out of memory errors on OS X during thread allocations. + // These choices are both for debuggability and to reduce the + // odds of the conservative garbage collector not collecting memory + // because some non-pointer block of memory had a bit pattern + // that matched a memory address. + // + // Actually we reserve 17 GB (because the bitmap ends up being 1 GB) + // but it hardly matters: fc is not valid UTF-8 either, and we have to + // allocate 15 GB before we get that far. + arena_size = 16LL<<30; + bitmap_size = arena_size / (sizeof(void*)*8/4); + p = runtime·SysReserve((void*)(0x00f8ULL<<32), bitmap_size + arena_size); + if(p == nil) + runtime·throw("runtime: cannot reserve arena virtual address space"); + } else { + // On a 32-bit machine, we can't typically get away + // with a giant virtual address space reservation. + // Instead we map the memory information bitmap + // immediately after the data segment, large enough + // to handle another 2GB of mappings (256 MB), + // along with a reservation for another 512 MB of memory. + // When that gets used up, we'll start asking the kernel + // for any memory anywhere and hope it's in the 2GB + // following the bitmap (presumably the executable begins + // near the bottom of memory, so we'll have to use up + // most of memory before the kernel resorts to giving out + // memory before the beginning of the text segment). + // + // Alternatively we could reserve 512 MB bitmap, enough + // for 4GB of mappings, and then accept any memory the + // kernel threw at us, but normally that's a waste of 512 MB + // of address space, which is probably too much in a 32-bit world. + bitmap_size = MaxArena32 / (sizeof(void*)*8/4); + arena_size = 512<<20; + + // SysReserve treats the address we ask for, end, as a hint, + // not as an absolute requirement. If we ask for the end + // of the data segment but the operating system requires + // a little more space before we can start allocating, it will + // give out a slightly higher pointer. That's fine. + // Run with what we get back. + p = runtime·SysReserve(end, bitmap_size + arena_size); + if(p == nil) + runtime·throw("runtime: cannot reserve arena virtual address space"); + } + if((uintptr)p & (((uintptr)1<<PageShift)-1)) + runtime·throw("runtime: SysReserve returned unaligned address"); + + runtime·mheap.bitmap = p; + runtime·mheap.arena_start = p + bitmap_size; + runtime·mheap.arena_used = runtime·mheap.arena_start; + runtime·mheap.arena_end = runtime·mheap.arena_start + arena_size; + + // Initialize the rest of the allocator. + runtime·MHeap_Init(&runtime·mheap, runtime·SysAlloc); + m->mcache = runtime·allocmcache(); + + // See if it works. + runtime·free(runtime·malloc(1)); +} + +void* +runtime·MHeap_SysAlloc(MHeap *h, uintptr n) +{ + byte *p; + + if(n <= h->arena_end - h->arena_used) { + // Keep taking from our reservation. + p = h->arena_used; + runtime·SysMap(p, n); + h->arena_used += n; + runtime·MHeap_MapBits(h); + return p; + } + + // On 64-bit, our reservation is all we have. + if(sizeof(void*) == 8) + return nil; + + // On 32-bit, once the reservation is gone we can + // try to get memory at a location chosen by the OS + // and hope that it is in the range we allocated bitmap for. + p = runtime·SysAlloc(n); + if(p == nil) + return nil; + + if(p < h->arena_start || p+n - h->arena_start >= MaxArena32) { + runtime·printf("runtime: memory allocated by OS not in usable range\n"); + runtime·SysFree(p, n); + return nil; + } + + if(p+n > h->arena_used) { + h->arena_used = p+n; + if(h->arena_used > h->arena_end) + h->arena_end = h->arena_used; + runtime·MHeap_MapBits(h); + } + + return p; +} + +// Runtime stubs. + +void* +runtime·mal(uintptr n) +{ + return runtime·mallocgc(n, 0, 1, 1); +} + +func new(n uint32) (ret *uint8) { + ret = runtime·mal(n); +} + +void* +runtime·stackalloc(uint32 n) +{ + // Stackalloc must be called on scheduler stack, so that we + // never try to grow the stack during the code that stackalloc runs. + // Doing so would cause a deadlock (issue 1547). + if(g != m->g0) + runtime·throw("stackalloc not on scheduler stack"); + + // Stack allocator uses malloc/free most of the time, + // but if we're in the middle of malloc and need stack, + // we have to do something else to avoid deadlock. + // In that case, we fall back on a fixed-size free-list + // allocator, assuming that inside malloc all the stack + // frames are small, so that all the stack allocations + // will be a single size, the minimum (right now, 5k). + if(m->mallocing || m->gcing || n == FixedStack) { + if(n != FixedStack) { + runtime·printf("stackalloc: in malloc, size=%d want %d", FixedStack, n); + runtime·throw("stackalloc"); + } + return runtime·FixAlloc_Alloc(m->stackalloc); + } + return runtime·mallocgc(n, FlagNoProfiling|FlagNoGC, 0, 0); +} + +void +runtime·stackfree(void *v, uintptr n) +{ + if(m->mallocing || m->gcing || n == FixedStack) { + runtime·FixAlloc_Free(m->stackalloc, v); + return; + } + runtime·free(v); +} + +func Alloc(n uintptr) (p *byte) { + p = runtime·malloc(n); +} + +func Free(p *byte) { + runtime·free(p); +} + +func Lookup(p *byte) (base *byte, size uintptr) { + runtime·mlookup(p, &base, &size, nil); +} + +func GC() { + runtime·gc(1); +} + +func SetFinalizer(obj Eface, finalizer Eface) { + byte *base; + uintptr size; + FuncType *ft; + int32 i, nret; + Type *t; + + if(obj.type == nil) { + runtime·printf("runtime.SetFinalizer: first argument is nil interface\n"); + throw: + runtime·throw("runtime.SetFinalizer"); + } + if(obj.type->kind != KindPtr) { + runtime·printf("runtime.SetFinalizer: first argument is %S, not pointer\n", *obj.type->string); + goto throw; + } + if(!runtime·mlookup(obj.data, &base, &size, nil) || obj.data != base) { + runtime·printf("runtime.SetFinalizer: pointer not at beginning of allocated block\n"); + goto throw; + } + nret = 0; + if(finalizer.type != nil) { + if(finalizer.type->kind != KindFunc) { + badfunc: + runtime·printf("runtime.SetFinalizer: second argument is %S, not func(%S)\n", *finalizer.type->string, *obj.type->string); + goto throw; + } + ft = (FuncType*)finalizer.type; + if(ft->dotdotdot || ft->in.len != 1 || *(Type**)ft->in.array != obj.type) + goto badfunc; + + // compute size needed for return parameters + for(i=0; i<ft->out.len; i++) { + t = ((Type**)ft->out.array)[i]; + nret = (nret + t->align - 1) & ~(t->align - 1); + nret += t->size; + } + nret = (nret + sizeof(void*)-1) & ~(sizeof(void*)-1); + + if(runtime·getfinalizer(obj.data, 0)) { + runtime·printf("runtime.SetFinalizer: finalizer already set\n"); + goto throw; + } + } + runtime·addfinalizer(obj.data, finalizer.data, nret); +} |