LEFT | RIGHT |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // Garbage collector (GC). | 5 // Garbage collector (GC). |
6 // | 6 // |
7 // GC is: | 7 // GC is: |
8 // - mark&sweep | 8 // - mark&sweep |
9 // - mostly precise (with the exception of some C-allocated objects, assembly fr
ames/arguments, etc) | 9 // - mostly precise (with the exception of some C-allocated objects, assembly fr
ames/arguments, etc) |
10 // - parallel (up to MaxGcproc threads) | 10 // - parallel (up to MaxGcproc threads) |
(...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
195 static struct { | 195 static struct { |
196 uint64 full; // lock-free list of full blocks | 196 uint64 full; // lock-free list of full blocks |
197 uint64 empty; // lock-free list of empty blocks | 197 uint64 empty; // lock-free list of empty blocks |
198 byte pad0[CacheLineSize]; // prevents false-sharing between full/empt
y and nproc/nwait | 198 byte pad0[CacheLineSize]; // prevents false-sharing between full/empt
y and nproc/nwait |
199 uint32 nproc; | 199 uint32 nproc; |
200 int64 tstart; | 200 int64 tstart; |
201 volatile uint32 nwait; | 201 volatile uint32 nwait; |
202 volatile uint32 ndone; | 202 volatile uint32 ndone; |
203 Note alldone; | 203 Note alldone; |
204 ParFor* markfor; | 204 ParFor* markfor; |
| 205 |
| 206 // Copy of mheap.allspans for marker or sweeper. |
| 207 MSpan** spans; |
| 208 uint32 nspan; |
205 } work; | 209 } work; |
206 | 210 |
207 // scanblock scans a block of n bytes starting at pointer b for references | 211 // scanblock scans a block of n bytes starting at pointer b for references |
208 // to other objects, scanning any it finds recursively until there are no | 212 // to other objects, scanning any it finds recursively until there are no |
209 // unscanned objects left. Instead of using an explicit recursion, it keeps | 213 // unscanned objects left. Instead of using an explicit recursion, it keeps |
210 // a work list in the Workbuf* structures and loops in the main function | 214 // a work list in the Workbuf* structures and loops in the main function |
211 // body. Keeping an explicit work list is easier on the stack allocator and | 215 // body. Keeping an explicit work list is easier on the stack allocator and |
212 // more efficient. | 216 // more efficient. |
213 static void | 217 static void |
214 scanblock(byte *b, uintptr n, byte *ptrmask) | 218 scanblock(byte *b, uintptr n, byte *ptrmask) |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
493 runtime·throw("scanblock: scanned invalid object
"); | 497 runtime·throw("scanblock: scanned invalid object
"); |
494 } | 498 } |
495 } | 499 } |
496 } | 500 } |
497 } | 501 } |
498 | 502 |
499 static void | 503 static void |
500 markroot(ParFor *desc, uint32 i) | 504 markroot(ParFor *desc, uint32 i) |
501 { | 505 { |
502 FinBlock *fb; | 506 FinBlock *fb; |
503 » MHeap *h; | 507 » MSpan *s; |
504 » MSpan **allspans, *s; | |
505 uint32 spanidx, sg; | 508 uint32 spanidx, sg; |
506 G *gp; | 509 G *gp; |
507 void *p; | 510 void *p; |
508 | 511 |
509 USED(&desc); | 512 USED(&desc); |
510 // Note: if you add a case here, please also update heapdump.c:dumproots
. | 513 // Note: if you add a case here, please also update heapdump.c:dumproots
. |
511 switch(i) { | 514 switch(i) { |
512 case RootData: | 515 case RootData: |
513 scanblock(data, edata - data, runtime·gcdatamask); | 516 scanblock(data, edata - data, runtime·gcdatamask); |
514 break; | 517 break; |
515 | 518 |
516 case RootBss: | 519 case RootBss: |
517 scanblock(bss, ebss - bss, runtime·gcbssmask); | 520 scanblock(bss, ebss - bss, runtime·gcbssmask); |
518 break; | 521 break; |
519 | 522 |
520 case RootFinalizers: | 523 case RootFinalizers: |
521 for(fb=allfin; fb; fb=fb->alllink) | 524 for(fb=allfin; fb; fb=fb->alllink) |
522 scanblock((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), Sc
anConservatively); | 525 scanblock((byte*)fb->fin, fb->cnt*sizeof(fb->fin[0]), Sc
anConservatively); |
523 break; | 526 break; |
524 | 527 |
525 case RootSpans: | 528 case RootSpans: |
526 // mark MSpan.specials | 529 // mark MSpan.specials |
527 » » h = &runtime·mheap; | 530 » » sg = runtime·mheap.sweepgen; |
528 » » sg = h->sweepgen; | 531 » » for(spanidx=0; spanidx<work.nspan; spanidx++) { |
529 » » allspans = h->allspans; | |
530 » » for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) { | |
531 Special *sp; | 532 Special *sp; |
532 SpecialFinalizer *spf; | 533 SpecialFinalizer *spf; |
533 | 534 |
534 » » » s = allspans[spanidx]; | 535 » » » s = work.spans[spanidx]; |
535 if(s->state != MSpanInUse) | 536 if(s->state != MSpanInUse) |
536 continue; | 537 continue; |
537 if(s->sweepgen != sg) { | 538 if(s->sweepgen != sg) { |
538 runtime·printf("sweep %d %d\n", s->sweepgen, sg)
; | 539 runtime·printf("sweep %d %d\n", s->sweepgen, sg)
; |
539 runtime·throw("gc: unswept span"); | 540 runtime·throw("gc: unswept span"); |
540 } | 541 } |
541 for(sp = s->specials; sp != nil; sp = sp->next) { | 542 for(sp = s->specials; sp != nil; sp = sp->next) { |
542 if(sp->kind != KindSpecialFinalizer) | 543 if(sp->kind != KindSpecialFinalizer) |
543 continue; | 544 continue; |
544 // don't mark finalized object, but scan it so w
e | 545 // don't mark finalized object, but scan it so w
e |
(...skipping 532 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1077 return res; | 1078 return res; |
1078 } | 1079 } |
1079 | 1080 |
1080 // State of background sweep. | 1081 // State of background sweep. |
1081 // Pretected by gclock. | 1082 // Pretected by gclock. |
1082 static struct | 1083 static struct |
1083 { | 1084 { |
1084 G* g; | 1085 G* g; |
1085 bool parked; | 1086 bool parked; |
1086 | 1087 |
1087 » MSpan**»spans; | 1088 » uint32» spanidx;» // background sweeper position |
1088 » uint32» nspan; | |
1089 » uint32» spanidx; | |
1090 | 1089 |
1091 uint32 nbgsweep; | 1090 uint32 nbgsweep; |
1092 uint32 npausesweep; | 1091 uint32 npausesweep; |
1093 } sweep; | 1092 } sweep; |
1094 | 1093 |
1095 // background sweeping goroutine | 1094 // background sweeping goroutine |
1096 static void | 1095 static void |
1097 bgsweep(void) | 1096 bgsweep(void) |
1098 { | 1097 { |
1099 g->issystem = 1; | 1098 g->issystem = 1; |
(...skipping 24 matching lines...) Expand all Loading... |
1124 MSpan *s; | 1123 MSpan *s; |
1125 uint32 idx, sg; | 1124 uint32 idx, sg; |
1126 uintptr npages; | 1125 uintptr npages; |
1127 | 1126 |
1128 // increment locks to ensure that the goroutine is not preempted | 1127 // increment locks to ensure that the goroutine is not preempted |
1129 // in the middle of sweep thus leaving the span in an inconsistent state
for next GC | 1128 // in the middle of sweep thus leaving the span in an inconsistent state
for next GC |
1130 g->m->locks++; | 1129 g->m->locks++; |
1131 sg = runtime·mheap.sweepgen; | 1130 sg = runtime·mheap.sweepgen; |
1132 for(;;) { | 1131 for(;;) { |
1133 idx = runtime·xadd(&sweep.spanidx, 1) - 1; | 1132 idx = runtime·xadd(&sweep.spanidx, 1) - 1; |
1134 » » if(idx >= sweep.nspan) { | 1133 » » if(idx >= work.nspan) { |
1135 runtime·mheap.sweepdone = true; | 1134 runtime·mheap.sweepdone = true; |
1136 g->m->locks--; | 1135 g->m->locks--; |
1137 return -1; | 1136 return -1; |
1138 } | 1137 } |
1139 » » s = sweep.spans[idx]; | 1138 » » s = work.spans[idx]; |
1140 if(s->state != MSpanInUse) { | 1139 if(s->state != MSpanInUse) { |
1141 s->sweepgen = sg; | 1140 s->sweepgen = sg; |
1142 continue; | 1141 continue; |
1143 } | 1142 } |
1144 if(s->sweepgen != sg-2 || !runtime·cas(&s->sweepgen, sg-2, sg-1)
) | 1143 if(s->sweepgen != sg-2 || !runtime·cas(&s->sweepgen, sg-2, sg-1)
) |
1145 continue; | 1144 continue; |
1146 npages = s->npages; | 1145 npages = s->npages; |
1147 if(!runtime·MSpan_Sweep(s, false)) | 1146 if(!runtime·MSpan_Sweep(s, false)) |
1148 npages = 0; | 1147 npages = 0; |
1149 g->m->locks--; | 1148 g->m->locks--; |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1252 // Flush MCache's to MCentral. | 1251 // Flush MCache's to MCentral. |
1253 if(g == g->m->g0) | 1252 if(g == g->m->g0) |
1254 flushallmcaches(); | 1253 flushallmcaches(); |
1255 else | 1254 else |
1256 runtime·mcall(flushallmcaches_m); | 1255 runtime·mcall(flushallmcaches_m); |
1257 | 1256 |
1258 // Aggregate local stats. | 1257 // Aggregate local stats. |
1259 cachestats(); | 1258 cachestats(); |
1260 | 1259 |
1261 // Scan all spans and count number of alive objects. | 1260 // Scan all spans and count number of alive objects. |
| 1261 runtime·lock(&runtime·mheap.lock); |
1262 for(i = 0; i < runtime·mheap.nspan; i++) { | 1262 for(i = 0; i < runtime·mheap.nspan; i++) { |
1263 s = runtime·mheap.allspans[i]; | 1263 s = runtime·mheap.allspans[i]; |
1264 if(s->state != MSpanInUse) | 1264 if(s->state != MSpanInUse) |
1265 continue; | 1265 continue; |
1266 if(s->sizeclass == 0) { | 1266 if(s->sizeclass == 0) { |
1267 mstats.nmalloc++; | 1267 mstats.nmalloc++; |
1268 mstats.alloc += s->elemsize; | 1268 mstats.alloc += s->elemsize; |
1269 } else { | 1269 } else { |
1270 mstats.nmalloc += s->ref; | 1270 mstats.nmalloc += s->ref; |
1271 mstats.by_size[s->sizeclass].nmalloc += s->ref; | 1271 mstats.by_size[s->sizeclass].nmalloc += s->ref; |
1272 mstats.alloc += s->ref*s->elemsize; | 1272 mstats.alloc += s->ref*s->elemsize; |
1273 } | 1273 } |
1274 } | 1274 } |
| 1275 runtime·unlock(&runtime·mheap.lock); |
1275 | 1276 |
1276 // Aggregate by size class. | 1277 // Aggregate by size class. |
1277 smallfree = 0; | 1278 smallfree = 0; |
1278 mstats.nfree = runtime·mheap.nlargefree; | 1279 mstats.nfree = runtime·mheap.nlargefree; |
1279 for(i = 0; i < nelem(mstats.by_size); i++) { | 1280 for(i = 0; i < nelem(mstats.by_size); i++) { |
1280 mstats.nfree += runtime·mheap.nsmallfree[i]; | 1281 mstats.nfree += runtime·mheap.nsmallfree[i]; |
1281 mstats.by_size[i].nfree = runtime·mheap.nsmallfree[i]; | 1282 mstats.by_size[i].nfree = runtime·mheap.nsmallfree[i]; |
1282 mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i]; | 1283 mstats.by_size[i].nmalloc += runtime·mheap.nsmallfree[i]; |
1283 smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size
[i]; | 1284 smallfree += runtime·mheap.nsmallfree[i] * runtime·class_to_size
[i]; |
1284 } | 1285 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1434 work.tstart = args->start_time;· | 1435 work.tstart = args->start_time;· |
1435 | 1436 |
1436 t1 = 0; | 1437 t1 = 0; |
1437 if(runtime·debug.gctrace) | 1438 if(runtime·debug.gctrace) |
1438 t1 = runtime·nanotime(); | 1439 t1 = runtime·nanotime(); |
1439 | 1440 |
1440 // Sweep what is not sweeped by bgsweep. | 1441 // Sweep what is not sweeped by bgsweep. |
1441 while(runtime·sweepone() != -1) | 1442 while(runtime·sweepone() != -1) |
1442 sweep.npausesweep++; | 1443 sweep.npausesweep++; |
1443 | 1444 |
| 1445 // Cache runtime.mheap.allspans in work.spans to avoid conflicts with |
| 1446 // resizing/freeing allspans. |
| 1447 // New spans can be created while GC progresses, but they are not garbag
e for |
| 1448 // this round: |
| 1449 // - new stack spans can be created even while the world is stopped. |
| 1450 // - new malloc spans can be created during the concurrent sweep |
| 1451 |
| 1452 // Even if this is stop-the-world, a concurrent exitsyscall can allocate
a stack from heap. |
| 1453 runtime·lock(&runtime·mheap.lock); |
| 1454 // Free the old cached sweep array if necessary. |
| 1455 if(work.spans != nil && work.spans != runtime·mheap.allspans) |
| 1456 runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &m
stats.other_sys); |
| 1457 // Cache the current array for marking. |
| 1458 runtime·mheap.gcspans = runtime·mheap.allspans; |
| 1459 work.spans = runtime·mheap.allspans; |
| 1460 work.nspan = runtime·mheap.nspan; |
| 1461 runtime·unlock(&runtime·mheap.lock); |
| 1462 |
1444 work.nwait = 0; | 1463 work.nwait = 0; |
1445 work.ndone = 0; | 1464 work.ndone = 0; |
1446 work.nproc = runtime·gcprocs(); | 1465 work.nproc = runtime·gcprocs(); |
1447 runtime·parforsetup(work.markfor, work.nproc, RootCount + runtime·allgle
n, nil, false, markroot); | 1466 runtime·parforsetup(work.markfor, work.nproc, RootCount + runtime·allgle
n, nil, false, markroot); |
1448 if(work.nproc > 1) { | 1467 if(work.nproc > 1) { |
1449 runtime·noteclear(&work.alldone); | 1468 runtime·noteclear(&work.alldone); |
1450 runtime·helpgc(work.nproc); | 1469 runtime·helpgc(work.nproc); |
1451 } | 1470 } |
1452 | 1471 |
1453 t2 = 0; | 1472 t2 = 0; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1493 stats.nprocyield += work.markfor->nprocyield; | 1512 stats.nprocyield += work.markfor->nprocyield; |
1494 stats.nosyield += work.markfor->nosyield; | 1513 stats.nosyield += work.markfor->nosyield; |
1495 stats.nsleep += work.markfor->nsleep; | 1514 stats.nsleep += work.markfor->nsleep; |
1496 | 1515 |
1497 runtime·printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D
) objects," | 1516 runtime·printf("gc%d(%d): %D+%D+%D+%D us, %D -> %D MB, %D (%D-%D
) objects," |
1498 " %d/%d/%d sweeps," | 1517 " %d/%d/%d sweeps," |
1499 " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\
n", | 1518 " %D(%D) handoff, %D(%D) steal, %D/%D/%D yields\
n", |
1500 mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t
3-t2)/1000, (t4-t3)/1000, | 1519 mstats.numgc, work.nproc, (t1-t0)/1000, (t2-t1)/1000, (t
3-t2)/1000, (t4-t3)/1000, |
1501 heap0>>20, heap1>>20, obj, | 1520 heap0>>20, heap1>>20, obj, |
1502 mstats.nmalloc, mstats.nfree, | 1521 mstats.nmalloc, mstats.nfree, |
1503 » » » sweep.nspan, sweep.nbgsweep, sweep.npausesweep, | 1522 » » » work.nspan, sweep.nbgsweep, sweep.npausesweep, |
1504 stats.nhandoff, stats.nhandoffcnt, | 1523 stats.nhandoff, stats.nhandoffcnt, |
1505 work.markfor->nsteal, work.markfor->nstealcnt, | 1524 work.markfor->nsteal, work.markfor->nstealcnt, |
1506 stats.nprocyield, stats.nosyield, stats.nsleep); | 1525 stats.nprocyield, stats.nosyield, stats.nsleep); |
1507 sweep.nbgsweep = sweep.npausesweep = 0; | 1526 sweep.nbgsweep = sweep.npausesweep = 0; |
1508 } | 1527 } |
1509 | 1528 |
1510 » // We cache current runtime·mheap.allspans array in sweep.spans, | 1529 » // See the comment in the beginning of this function as to why we need t
he following. |
1511 » // because the former can be resized and freed. | 1530 » // Even if this is still stop-the-world, a concurrent exitsyscall can al
locate a stack from heap. |
1512 » // Otherwise we would need to take heap lock every time | 1531 » runtime·lock(&runtime·mheap.lock); |
1513 » // we want to convert span index to span pointer. | 1532 » // Free the old cached mark array if necessary. |
1514 | 1533 » if(work.spans != nil && work.spans != runtime·mheap.allspans) |
1515 » // Free the old cached array if necessary. | 1534 » » runtime·SysFree(work.spans, work.nspan*sizeof(work.spans[0]), &m
stats.other_sys); |
1516 » if(sweep.spans && sweep.spans != runtime·mheap.allspans) | 1535 » // Cache the current array for sweeping. |
1517 » » runtime·SysFree(sweep.spans, sweep.nspan*sizeof(sweep.spans[0]),
&mstats.other_sys); | 1536 » runtime·mheap.gcspans = runtime·mheap.allspans; |
1518 » // Cache the current array. | |
1519 » runtime·mheap.sweepspans = runtime·mheap.allspans; | |
1520 runtime·mheap.sweepgen += 2; | 1537 runtime·mheap.sweepgen += 2; |
1521 runtime·mheap.sweepdone = false; | 1538 runtime·mheap.sweepdone = false; |
1522 » sweep.spans = runtime·mheap.allspans; | 1539 » work.spans = runtime·mheap.allspans; |
1523 » sweep.nspan = runtime·mheap.nspan; | 1540 » work.nspan = runtime·mheap.nspan; |
1524 sweep.spanidx = 0; | 1541 sweep.spanidx = 0; |
| 1542 runtime·unlock(&runtime·mheap.lock); |
1525 | 1543 |
1526 // Temporary disable concurrent sweep, because we see failures on builde
rs. | 1544 // Temporary disable concurrent sweep, because we see failures on builde
rs. |
1527 if(ConcurrentSweep && !args->eagersweep) { | 1545 if(ConcurrentSweep && !args->eagersweep) { |
1528 runtime·lock(&gclock); | 1546 runtime·lock(&gclock); |
1529 if(sweep.g == nil) | 1547 if(sweep.g == nil) |
1530 sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, runtime
·gc); | 1548 sweep.g = runtime·newproc1(&bgsweepv, nil, 0, 0, runtime
·gc); |
1531 else if(sweep.parked) { | 1549 else if(sweep.parked) { |
1532 sweep.parked = false; | 1550 sweep.parked = false; |
1533 runtime·ready(sweep.g); | 1551 runtime·ready(sweep.g); |
1534 } | 1552 } |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1596 for(i=0; i<n; i++) | 1614 for(i=0; i<n; i++) |
1597 p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)
]; | 1615 p[i] = mstats.pause_ns[(mstats.numgc-1-i)%nelem(mstats.pause_ns)
]; |
1598 | 1616 |
1599 p[n] = mstats.last_gc; | 1617 p[n] = mstats.last_gc; |
1600 p[n+1] = mstats.numgc; | 1618 p[n+1] = mstats.numgc; |
1601 p[n+2] = mstats.pause_total_ns;· | 1619 p[n+2] = mstats.pause_total_ns;· |
1602 runtime·unlock(&runtime·mheap.lock); | 1620 runtime·unlock(&runtime·mheap.lock); |
1603 pauses->len = n+3; | 1621 pauses->len = n+3; |
1604 } | 1622 } |
1605 | 1623 |
1606 int32 | 1624 void |
1607 runtime·setgcpercent(int32 in) { | 1625 runtime·setgcpercent_m(void) { |
| 1626 » int32 in; |
1608 int32 out; | 1627 int32 out; |
| 1628 |
| 1629 in = (int32)(intptr)g->m->scalararg[0]; |
1609 | 1630 |
1610 runtime·lock(&runtime·mheap.lock); | 1631 runtime·lock(&runtime·mheap.lock); |
1611 out = runtime·gcpercent; | 1632 out = runtime·gcpercent; |
1612 if(in < 0) | 1633 if(in < 0) |
1613 in = -1; | 1634 in = -1; |
1614 runtime·gcpercent = in; | 1635 runtime·gcpercent = in; |
1615 runtime·unlock(&runtime·mheap.lock); | 1636 runtime·unlock(&runtime·mheap.lock); |
1616 » return out; | 1637 |
| 1638 » g->m->scalararg[0] = (uintptr)(intptr)out; |
1617 } | 1639 } |
1618 | 1640 |
1619 static void | 1641 static void |
1620 gchelperstart(void) | 1642 gchelperstart(void) |
1621 { | 1643 { |
1622 if(g->m->helpgc < 0 || g->m->helpgc >= MaxGcproc) | 1644 if(g->m->helpgc < 0 || g->m->helpgc >= MaxGcproc) |
1623 runtime·throw("gchelperstart: bad m->helpgc"); | 1645 runtime·throw("gchelperstart: bad m->helpgc"); |
1624 if(g != g->m->g0) | 1646 if(g != g->m->g0) |
1625 runtime·throw("gchelper not running on g0 stack"); | 1647 runtime·throw("gchelper not running on g0 stack"); |
1626 } | 1648 } |
(...skipping 484 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2111 | 2133 |
2112 void runtime·gc_unixnanotime(int64 *now); | 2134 void runtime·gc_unixnanotime(int64 *now); |
2113 | 2135 |
2114 int64 runtime·unixnanotime(void) | 2136 int64 runtime·unixnanotime(void) |
2115 { | 2137 { |
2116 int64 now; | 2138 int64 now; |
2117 | 2139 |
2118 runtime·gc_unixnanotime(&now); | 2140 runtime·gc_unixnanotime(&now); |
2119 return now; | 2141 return now; |
2120 } | 2142 } |
LEFT | RIGHT |