gc #0. mark and sweep collector.
R=r,gri
DELTA=472 (423 added, 2 deleted, 47 changed)
OCL=23522
CL=23541
diff --git a/src/lib/malloc.go b/src/lib/malloc.go
index e3896e9..8e4397a 100644
--- a/src/lib/malloc.go
+++ b/src/lib/malloc.go
@@ -11,9 +11,14 @@
type Stats struct {
Alloc uint64;
Sys uint64;
-};
+ Stacks uint64;
+ InusePages uint64;
+ NextGC uint64;
+ EnableGC bool;
+}
-func Alloc(uint64) *byte;
-func Free(*byte);
-func GetStats() *Stats;
-func Lookup(*byte) (*byte, uintptr);
+func Alloc(uint64) *byte
+func Free(*byte)
+func GetStats() *Stats
+func Lookup(*byte) (*byte, uintptr)
+func GC()
diff --git a/src/runtime/Makefile b/src/runtime/Makefile
index 03633a6..e9c895a 100644
--- a/src/runtime/Makefile
+++ b/src/runtime/Makefile
@@ -29,6 +29,7 @@
mcentral.$O\
mem.$O\
mfixalloc.$O\
+ mgc0.$O\
mheap.$O\
msize.$O\
print.$O\
diff --git a/src/runtime/malloc.c b/src/runtime/malloc.c
index 258291d..e518b56 100644
--- a/src/runtime/malloc.c
+++ b/src/runtime/malloc.c
@@ -24,6 +24,7 @@
uintptr npages;
MSpan *s;
void *v;
+ uint32 *ref;
if(m->mallocing)
throw("malloc - deadlock");
@@ -55,10 +56,25 @@
v = (void*)(s->start << PageShift);
}
+ // setup for mark sweep
+ mlookup(v, nil, nil, &ref);
+ *ref = RefNone;
+
m->mallocing = 0;
return v;
}
+void*
+mallocgc(uintptr size)
+{
+ void *v;
+
+ v = malloc(size);
+ if(mstats.inuse_pages > mstats.next_gc)
+ gc(0);
+ return v;
+}
+
// Free the object whose base pointer is v.
void
free(void *v)
@@ -67,10 +83,14 @@
uintptr page, tmp;
MSpan *s;
MCache *c;
+ uint32 *ref;
if(v == nil)
return;
+ mlookup(v, nil, nil, &ref);
+ *ref = RefFree;
+
// Find size class for v.
page = (uintptr)v >> PageShift;
sizeclass = MHeapMapCache_GET(&mheap.mapcache, page, tmp);
@@ -98,32 +118,51 @@
MCache_Free(c, v, sizeclass, size);
}
-void
-mlookup(void *v, byte **base, uintptr *size)
+int32
+mlookup(void *v, byte **base, uintptr *size, uint32 **ref)
{
- uintptr n, off;
+ uintptr n, i;
byte *p;
MSpan *s;
- s = MHeap_Lookup(&mheap, (uintptr)v>>PageShift);
+ s = MHeap_LookupMaybe(&mheap, (uintptr)v>>PageShift);
if(s == nil) {
- *base = nil;
- *size = 0;
- return;
+ if(base)
+ *base = nil;
+ if(size)
+ *size = 0;
+ if(ref)
+ *ref = 0;
+ return 0;
}
p = (byte*)((uintptr)s->start<<PageShift);
if(s->sizeclass == 0) {
// Large object.
- *base = p;
- *size = s->npages<<PageShift;
- return;
+ if(base)
+ *base = p;
+ if(size)
+ *size = s->npages<<PageShift;
+ if(ref)
+ *ref = &s->gcref0;
+ return 1;
}
n = class_to_size[s->sizeclass];
- off = ((byte*)v - p)/n * n;
- *base = p+off;
- *size = n;
+ i = ((byte*)v - p)/n;
+ if(base)
+ *base = p + i*n;
+ if(size)
+ *size = n;
+ if((byte*)s->gcref < p || (byte*)s->gcref >= p+(s->npages<<PageShift)) {
+ printf("s->base sizeclass %d %p gcref %p block %D\n",
+ s->sizeclass, p, s->gcref, s->npages<<PageShift);
+ throw("bad gcref");
+ }
+ if(ref)
+ *ref = &s->gcref[i];
+
+ return 1;
}
MCache*
@@ -193,7 +232,7 @@
//return oldmal(n);
void *v;
- v = malloc(n);
+ v = mallocgc(n);
if(0) {
byte *p;
@@ -227,6 +266,7 @@
stackalloc(uint32 n)
{
void *v;
+ uint32 *ref;
//return oldmal(n);
if(m->mallocing) {
@@ -241,7 +281,10 @@
unlock(&stacks);
return v;
}
- return malloc(n);
+ v = malloc(n);
+ mlookup(v, nil, nil, &ref);
+ *ref = RefStack;
+ return v;
}
void
diff --git a/src/runtime/malloc.h b/src/runtime/malloc.h
index ca05f01..1da9f98 100644
--- a/src/runtime/malloc.h
+++ b/src/runtime/malloc.h
@@ -91,7 +91,7 @@
enum
{
// Tunable constants.
- NumSizeClasses = 133, // Number of size classes (must match msize.c)
+ NumSizeClasses = 150, // Number of size classes (must match msize.c)
MaxSmallSize = 32<<10,
FixAllocChunk = 128<<10, // Chunk size for FixAlloc
@@ -152,6 +152,9 @@
uint64 alloc;
uint64 sys;
uint64 stacks;
+ uint64 inuse_pages; // protected by mheap.Lock
+ uint64 next_gc; // protected by mheap.Lock
+ bool enablegc;
};
extern MStats mstats;
@@ -212,6 +215,10 @@
uint32 ref; // number of allocated objects in this span
uint32 sizeclass; // size class
uint32 state; // MSpanInUse or MSpanFree
+ union {
+ uint32 *gcref; // sizeclass > 0
+ uint32 gcref0; // sizeclass == 0
+ };
};
void MSpan_Init(MSpan *span, PageID start, uintptr npages);
@@ -292,6 +299,7 @@
void MHeapMap_Init(MHeapMap *m, void *(*allocator)(uintptr));
bool MHeapMap_Preallocate(MHeapMap *m, PageID k, uintptr npages);
MSpan* MHeapMap_Get(MHeapMap *m, PageID k);
+MSpan* MHeapMap_GetMaybe(MHeapMap *m, PageID k);
void MHeapMap_Set(MHeapMap *m, PageID k, MSpan *v);
@@ -364,7 +372,19 @@
MSpan* MHeap_Alloc(MHeap *h, uintptr npage, int32 sizeclass);
void MHeap_Free(MHeap *h, MSpan *s);
MSpan* MHeap_Lookup(MHeap *h, PageID p);
+MSpan* MHeap_LookupMaybe(MHeap *h, PageID p);
-void* malloc(uintptr size);
-void free(void *v);
-void mlookup(void *v, byte **base, uintptr *size);
+int32 mlookup(void *v, byte **base, uintptr *size, uint32 **ref);
+void gc(int32 force);
+
+enum
+{
+ RefcountOverhead = 4, // one uint32 per object
+
+ RefFree = 0, // must be zero
+ RefManual, // manual allocation - don't free
+ RefStack, // stack segment - don't free and don't scan for pointers
+ RefNone, // no references
+ RefSome, // some references
+};
+
diff --git a/src/runtime/malloc_go.cgo b/src/runtime/malloc_go.cgo
index 7c55c107..6dcdaec 100644
--- a/src/runtime/malloc_go.cgo
+++ b/src/runtime/malloc_go.cgo
@@ -15,9 +15,14 @@
}
func Lookup(p *byte) (base *byte, size uintptr) {
- mlookup(p, &base, &size);
+ mlookup(p, &base, &size, nil);
}
func GetStats() (s *MStats) {
s = &mstats;
}
+
+func GC() {
+ gc(1);
+}
+
diff --git a/src/runtime/mcentral.c b/src/runtime/mcentral.c
index 5b07faf..5c9f720 100644
--- a/src/runtime/mcentral.c
+++ b/src/runtime/mcentral.c
@@ -157,9 +157,9 @@
static bool
MCentral_Grow(MCentral *c)
{
- int32 n, npages, size;
+ int32 i, n, npages, size;
MLink **tailp, *v;
- byte *p, *end;
+ byte *p;
MSpan *s;
unlock(c);
@@ -174,14 +174,14 @@
// Carve span into sequence of blocks.
tailp = &s->freelist;
p = (byte*)(s->start << PageShift);
- end = p + (npages << PageShift);
size = class_to_size[c->sizeclass];
- n = 0;
- for(; p + size <= end; p += size) {
+ n = (npages << PageShift) / (size + RefcountOverhead);
+ s->gcref = (uint32*)(p + size*n);
+ for(i=0; i<n; i++) {
v = (MLink*)p;
*tailp = v;
tailp = &v->next;
- n++;
+ p += size;
}
*tailp = nil;
diff --git a/src/runtime/mgc0.c b/src/runtime/mgc0.c
new file mode 100644
index 0000000..3584bf7
--- /dev/null
+++ b/src/runtime/mgc0.c
@@ -0,0 +1,246 @@
+// 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.
+
+// Garbage collector -- step 0.
+//
+// Stop the world, mark and sweep garbage collector.
+// NOT INTENDED FOR PRODUCTION USE.
+//
+// A mark and sweep collector provides a way to exercise
+// and test the memory allocator and the stack walking machinery
+// without also needing to get reference counting
+// exactly right.
+
+#include "runtime.h"
+#include "malloc.h"
+
+enum {
+ Debug = 0
+};
+
+extern byte etext[];
+extern byte end[];
+
+static void
+scanblock(int32 depth, byte *b, int64 n)
+{
+ int32 off;
+ void *obj;
+ uintptr size;
+ uint32 *ref;
+ void **vp;
+ int64 i;
+
+ if(Debug)
+ printf("%d scanblock %p %D\n", depth, b, n);
+ off = (uint32)(uintptr)b & 7;
+ if(off) {
+ b += 8 - off;
+ n -= 8 - off;
+ }
+
+ vp = (void**)b;
+ n /= 8;
+ for(i=0; i<n; i++) {
+ if(mlookup(vp[i], &obj, &size, &ref)) {
+ if(*ref == RefFree || *ref == RefStack)
+ continue;
+ if(*ref == RefNone) {
+ if(Debug)
+ printf("%d found at %p: ", depth, &vp[i]);
+ *ref = RefSome;
+ scanblock(depth+1, obj, size);
+ }
+ }
+ }
+}
+
+static void
+scanstack(G *g)
+{
+ Stktop *stk;
+ byte *sp;
+
+ sp = g->sched.SP;
+ stk = (Stktop*)g->stackbase;
+ while(stk) {
+ scanblock(0, sp, (byte*)stk - sp);
+ sp = stk->oldsp;
+ stk = (Stktop*)stk->oldbase;
+ }
+}
+
+static void
+mark(void)
+{
+ G *gp;
+
+ // mark data+bss
+ scanblock(0, etext, end - etext);
+
+ // mark stacks
+ for(gp=allg; gp!=nil; gp=gp->alllink) {
+ switch(gp->status){
+ default:
+ printf("unexpected G.status %d\n", gp->status);
+ throw("mark - bad status");
+ case Gdead:
+ break;
+ case Grunning:
+ if(gp != g)
+ throw("mark - world not stopped");
+ scanstack(gp);
+ break;
+ case Grunnable:
+ case Gsyscall:
+ case Gwaiting:
+ scanstack(gp);
+ break;
+ }
+ }
+}
+
+static void
+sweepspan(MSpan *s)
+{
+ int32 i, n, npages, size;
+ byte *p;
+
+ if(s->state != MSpanInUse)
+ return;
+
+ p = (byte*)(s->start << PageShift);
+ if(s->sizeclass == 0) {
+ // Large block.
+ switch(s->gcref0) {
+ default:
+ throw("bad 'ref count'");
+ case RefFree:
+ case RefManual:
+ case RefStack:
+ break;
+ case RefNone:
+ if(Debug)
+ printf("free %D at %p\n", s->npages<<PageShift, p);
+ free(p);
+ break;
+ case RefSome:
+ s->gcref0 = RefNone; // set up for next mark phase
+ break;
+ }
+ return;
+ }
+
+ // Chunk full of small blocks.
+ // Must match computation in MCentral_Grow.
+ size = class_to_size[s->sizeclass];
+ npages = class_to_allocnpages[s->sizeclass];
+ n = (npages << PageShift) / (size + RefcountOverhead);
+ for(i=0; i<n; i++) {
+ switch(s->gcref[i]) {
+ default:
+ throw("bad 'ref count'");
+ case RefFree:
+ case RefManual:
+ case RefStack:
+ break;
+ case RefNone:
+ if(Debug)
+ printf("free %d at %p\n", size, p+i*size);
+ free(p + i*size);
+ break;
+ case RefSome:
+ s->gcref[i] = RefNone; // set up for next mark phase
+ break;
+ }
+ }
+}
+
+static void
+sweepspanlist(MSpan *list)
+{
+ MSpan *s, *next;
+
+ for(s=list->next; s != list; s=next) {
+ next = s->next; // in case s gets moved
+ sweepspan(s);
+ }
+}
+
+static void
+sweep(void)
+{
+ int32 i;
+
+ // Sweep all the spans.
+
+ for(i=0; i<nelem(mheap.central); i++) {
+ // Sweep nonempty (has some free blocks available)
+ // before sweeping empty (is completely allocated),
+ // because finding something to free in a span from empty
+ // will move it into nonempty, and we must not sweep
+ // the same span twice.
+ sweepspanlist(&mheap.central[i].nonempty);
+ sweepspanlist(&mheap.central[i].empty);
+ }
+}
+
+// Semaphore, not Lock, so that the goroutine
+// reschedules when there is contention rather
+// than spinning.
+static uint32 gcsema = 1;
+
+// Initialized from $GOGC. GOGC=off means no gc.
+//
+// Next gc is after we've allocated an extra amount of
+// memory proportional to the amount already in use.
+// If gcpercent=100 and we're using 4M, we'll gc again
+// when we get to 8M. This keeps the gc cost in linear
+// proportion to the allocation cost. Adjusting gcpercent
+// just changes the linear constant (and also the amount of
+// extra memory used).
+static int32 gcpercent = -2;
+
+void
+gc(int32 force)
+{
+ byte *p;
+
+ // The gc is turned off (via enablegc) until
+ // the bootstrap has completed.
+ // Also, malloc gets called in the guts
+ // of a number of libraries that might be
+ // holding locks. To avoid priority inversion
+ // problems, don't bother trying to run gc
+ // while holding a lock. The next mallocgc
+ // without a lock will do the gc instead.
+ if(!mstats.enablegc || m->locks > 0 || panicking)
+ return;
+
+ if(gcpercent == -2) { // first time through
+ p = getenv("GOGC");
+ if(p == nil || p[0] == '\0')
+ gcpercent = 100;
+ else if(strcmp(p, (byte*)"off") == 0)
+ gcpercent = -1;
+ else
+ gcpercent = atoi(p);
+ }
+ if(gcpercent < 0)
+ return;
+
+ semacquire(&gcsema);
+ gosave(&g->sched); // update g's stack pointer for scanstack
+ stoptheworld();
+ if(mheap.Lock.key != 0)
+ throw("mheap locked during gc");
+ if(force || mstats.inuse_pages >= mstats.next_gc) {
+ mark();
+ sweep();
+ mstats.next_gc = mstats.inuse_pages+mstats.inuse_pages*gcpercent/100;
+ }
+ starttheworld();
+ gosave(&g->sched); // update g's stack pointer for debugging
+ semrelease(&gcsema);
+}
diff --git a/src/runtime/mheap.c b/src/runtime/mheap.c
index 9f8e916..d1b504e 100644
--- a/src/runtime/mheap.c
+++ b/src/runtime/mheap.c
@@ -47,6 +47,8 @@
lock(h);
s = MHeap_AllocLocked(h, npage, sizeclass);
+ if(s != nil)
+ mstats.inuse_pages += npage;
unlock(h);
return s;
}
@@ -108,6 +110,11 @@
for(n=0; n<npage; n++)
if(MHeapMapCache_GET(&h->mapcache, s->start+n, tmp) != 0)
MHeapMapCache_SET(&h->mapcache, s->start+n, 0);
+
+ // Need a list of large allocated spans.
+ // They have sizeclass == 0, so use heap.central[0].empty,
+ // since central[0] is otherwise unused.
+ MSpanList_Insert(&h->central[0].empty, s);
} else {
// Save cache entries for this span.
// If there's a size class, there aren't that many pages.
@@ -191,17 +198,38 @@
}
// Look up the span at the given page number.
+// Page number is guaranteed to be in map
+// and is guaranteed to be start or end of span.
MSpan*
MHeap_Lookup(MHeap *h, PageID p)
{
return MHeapMap_Get(&h->map, p);
}
+// Look up the span at the given page number.
+// Page number is *not* guaranteed to be in map
+// and may be anywhere in the span.
+// Map entries for the middle of a span are only
+// valid for allocated spans. Free spans may have
+// other garbage in their middles, so we have to
+// check for that.
+MSpan*
+MHeap_LookupMaybe(MHeap *h, PageID p)
+{
+ MSpan *s;
+
+ s = MHeapMap_GetMaybe(&h->map, p);
+ if(s == nil || p < s->start || p - s->start >= s->npages)
+ return nil;
+ return s;
+}
+
// Free the span back into the heap.
void
MHeap_Free(MHeap *h, MSpan *s)
{
lock(h);
+ mstats.inuse_pages -= s->npages;
MHeap_FreeLocked(h, s);
unlock(h);
}
@@ -266,6 +294,31 @@
return m->p[i1]->p[i2]->s[i3];
}
+MSpan*
+MHeapMap_GetMaybe(MHeapMap *m, PageID k)
+{
+ int32 i1, i2, i3;
+ MHeapMapNode2 *p2;
+ MHeapMapNode3 *p3;
+
+ i3 = k & MHeapMap_Level3Mask;
+ k >>= MHeapMap_Level3Bits;
+ i2 = k & MHeapMap_Level2Mask;
+ k >>= MHeapMap_Level2Bits;
+ i1 = k & MHeapMap_Level1Mask;
+ k >>= MHeapMap_Level1Bits;
+ if(k != 0)
+ throw("MHeapMap_Get");
+
+ p2 = m->p[i1];
+ if(p2 == nil)
+ return nil;
+ p3 = p2->p[i2];
+ if(p3 == nil)
+ return nil;
+ return p3->s[i3];
+}
+
void
MHeapMap_Set(MHeapMap *m, PageID k, MSpan *s)
{
diff --git a/src/runtime/msize.c b/src/runtime/msize.c
index ff1ca72..62d5c3a 100644
--- a/src/runtime/msize.c
+++ b/src/runtime/msize.c
@@ -57,7 +57,7 @@
void
InitSizes(void)
{
- int32 align, sizeclass, size, nextsize, n;
+ int32 align, sizeclass, size, osize, nextsize, n;
uint32 i;
uintptr allocsize, npages;
@@ -81,7 +81,8 @@
// the leftover is less than 1/8 of the total,
// so wasted space is at most 12.5%.
allocsize = PageSize;
- while(allocsize%size > (PageSize/8))
+ osize = size + RefcountOverhead;
+ while(allocsize%osize > (PageSize/8))
allocsize += PageSize;
npages = allocsize >> PageShift;
@@ -92,7 +93,7 @@
// different sizes.
if(sizeclass > 1
&& npages == class_to_allocnpages[sizeclass-1]
- && allocsize/size == allocsize/class_to_size[sizeclass-1]) {
+ && allocsize/osize == allocsize/(class_to_size[sizeclass-1]+RefcountOverhead)) {
class_to_size[sizeclass-1] = size;
continue;
}
diff --git a/src/runtime/proc.c b/src/runtime/proc.c
index 943792f..7435830ff 100644
--- a/src/runtime/proc.c
+++ b/src/runtime/proc.c
@@ -3,7 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
-#include "malloc.h" /* so that acid generated from proc.c includes malloc data structures */
+#include "malloc.h"
typedef struct Sched Sched;
@@ -118,6 +118,7 @@
{
// Let's go.
sched.predawn = 0;
+ mstats.enablegc = 1;
// If main·init_function started other goroutines,
// kick off new ms to handle them, like ready
@@ -146,7 +147,7 @@
byte *stk;
// 160 is the slop amount known to the stack growth code
- g = mal(sizeof(G));
+ g = malloc(sizeof(G));
stk = stackalloc(160 + stacksize);
g->stack0 = stk;
g->stackguard = stk + 160;
@@ -444,7 +445,7 @@
m->nextg = g;
notewakeup(&m->havenextg);
}else{
- m = mal(sizeof(M));
+ m = malloc(sizeof(M));
m->g0 = malg(8192);
m->nextg = g;
m->id = sched.mcount++;
@@ -525,6 +526,8 @@
void
sys·Gosched(void)
{
+ if(g == m->g0)
+ throw("gosched of g0");
if(gosave(&g->sched) == 0){
g = m->g0;
gogo(&m->sched);
diff --git a/src/runtime/rt1_amd64_darwin.c b/src/runtime/rt1_amd64_darwin.c
index 453bd51..b614756 100644
--- a/src/runtime/rt1_amd64_darwin.c
+++ b/src/runtime/rt1_amd64_darwin.c
@@ -130,6 +130,7 @@
{
if(panicking) // traceback already printed
sys_Exit(2);
+ panicking = 1;
_STRUCT_MCONTEXT64 *uc_mcontext = get_uc_mcontext(context);
_STRUCT_X86_THREAD_STATE64 *ss = get___ss(uc_mcontext);
@@ -282,11 +283,13 @@
if(xadd(&l->key, 1) > 1) // someone else has it; wait
mach_semacquire(l->sema);
+ m->locks++;
}
void
unlock(Lock *l)
{
+ m->locks--;
if(xadd(&l->key, -1) > 0) // someone else is waiting
mach_semrelease(l->sema);
}
diff --git a/src/runtime/rt1_amd64_linux.c b/src/runtime/rt1_amd64_linux.c
index 74032e4..c0c2038 100644
--- a/src/runtime/rt1_amd64_linux.c
+++ b/src/runtime/rt1_amd64_linux.c
@@ -306,6 +306,8 @@
{
uint32 v;
+ m->locks++;
+
again:
v = l->key;
if((v&1) == 0){
@@ -349,6 +351,8 @@
{
uint32 v;
+ m->locks--;
+
// Atomically get value and clear lock bit.
again:
v = l->key;
diff --git a/src/runtime/runtime.c b/src/runtime/runtime.c
index 00e3638..a972b75 100644
--- a/src/runtime/runtime.c
+++ b/src/runtime/runtime.c
@@ -147,21 +147,25 @@
void
goargs(void)
{
- string* goargv;
- string* envv;
+ string *gargv;
+ string *genvv;
int32 i, envc;
- goargv = (string*)argv;
- for (i=0; i<argc; i++)
- goargv[i] = gostring(argv[i]);
- sys·Args.array = (byte*)argv;
+ for(envc=0; argv[argc+1+envc] != 0; envc++)
+ ;
+
+ gargv = malloc(argc*sizeof gargv[0]);
+ genvv = malloc(envc*sizeof genvv[0]);
+
+ for(i=0; i<argc; i++)
+ gargv[i] = gostring(argv[i]);
+ sys·Args.array = (byte*)gargv;
sys·Args.nel = argc;
sys·Args.cap = argc;
- envv = goargv + argc + 1; // skip 0 at end of argv
- for (envc = 0; envv[envc] != 0; envc++)
- envv[envc] = gostring((uint8*)envv[envc]);
- sys·Envs.array = (byte*)envv;
+ for(i=0; i<envc; i++)
+ genvv[i] = gostring(argv[argc+1+i]);
+ sys·Envs.array = (byte*)genvv;
sys·Envs.nel = envc;
sys·Envs.cap = envc;
}
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 170657d..5552c9e 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -162,6 +162,7 @@
int32 siz2;
int32 id;
int32 mallocing;
+ int32 locks;
Note havenextg;
G* nextg;
M* schedlink;
@@ -304,6 +305,9 @@
uint64 ifacehash(Iface);
uint64 nohash(uint32, void*);
uint32 noequal(uint32, void*, void*);
+void* malloc(uintptr size);
+void* mallocgc(uintptr size);
+void free(void *v);
#pragma varargck argpos printf 1
diff --git a/test/gc.go b/test/gc.go
new file mode 100644
index 0000000..df9d05e
--- /dev/null
+++ b/test/gc.go
@@ -0,0 +1,25 @@
+// $G $F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+package main
+
+import "malloc"
+
+func mk2() {
+ b := new([10000]byte);
+// println(b, "stored at", &b);
+}
+
+func mk1() {
+ mk2();
+}
+
+func main() {
+ for i := 0; i < 10; i++ {
+ mk1();
+ malloc.GC();
+ }
+}
diff --git a/test/gc1.go b/test/gc1.go
new file mode 100644
index 0000000..d746e9c
--- /dev/null
+++ b/test/gc1.go
@@ -0,0 +1,13 @@
+// $G $F.go && $L $F.$A && ./$A.out
+
+// 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.
+
+package main
+
+func main() {
+ for i := 0; i < 1000000; i++ {
+ x := new([100]byte);
+ }
+}