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 // Page heap. | 5 // Page heap. |
6 // | 6 // |
7 // See malloc.h for overview. | 7 // See malloc.h for overview. |
8 // | 8 // |
9 // When a MSpan is in the heap free list, state == MSpanFree | 9 // When a MSpan is in the heap free list, state == MSpanFree |
10 // and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span. | 10 // and heapmap(s->start) == span, heapmap(s->start+s->npages-1) == span. |
11 // | 11 // |
12 // When a MSpan is allocated, state == MSpanInUse | 12 // When a MSpan is allocated, state == MSpanInUse |
13 // and heapmap(i) == span for all s->start <= i < s->start+s->npages. | 13 // and heapmap(i) == span for all s->start <= i < s->start+s->npages. |
14 | 14 |
15 #include "runtime.h" | 15 #include "runtime.h" |
16 #include "arch.h" | 16 #include "arch_GOARCH.h" |
17 #include "malloc.h" | 17 #include "malloc.h" |
18 | 18 |
19 static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32); | 19 static MSpan *MHeap_AllocLocked(MHeap*, uintptr, int32); |
20 static bool MHeap_Grow(MHeap*, uintptr); | 20 static bool MHeap_Grow(MHeap*, uintptr); |
21 static void MHeap_FreeLocked(MHeap*, MSpan*); | 21 static void MHeap_FreeLocked(MHeap*, MSpan*); |
22 static MSpan *MHeap_AllocLarge(MHeap*, uintptr); | 22 static MSpan *MHeap_AllocLarge(MHeap*, uintptr); |
23 static MSpan *BestFit(MSpan*, uintptr, MSpan*); | 23 static MSpan *BestFit(MSpan*, uintptr, MSpan*); |
24 | 24 |
25 static void | 25 static void |
26 RecordSpan(void *vh, byte *p) | 26 RecordSpan(void *vh, byte *p) |
27 { | 27 { |
28 MHeap *h; | 28 MHeap *h; |
29 MSpan *s; | 29 MSpan *s; |
30 | 30 » MSpan **all; |
| 31 » uintptr cap; |
| 32 »······· |
31 h = vh; | 33 h = vh; |
32 s = (MSpan*)p; | 34 s = (MSpan*)p; |
33 » //s->allnext = h->allspans; | 35 » if(h->nspan >= h->nspancap) { |
34 » //h->allspans = s; | 36 » » cap = 64*1024/sizeof(MSpan*); |
35 » if(h->nspan == nelem(h->allspans)) | 37 » » if(cap < h->nspancap*3/2) |
36 » » runtime·throw("span overflow"); | 38 » » » cap = h->nspancap*3/2; |
37 » h->allspans[h->nspan] = s; | 39 » » all = (MSpan**)runtime·SysAlloc(cap*sizeof(MSpan*)); |
38 » h->nspan++; | 40 » » if(h->allspans) { |
| 41 » » » runtime·memmove(all, h->allspans, h->nspancap*sizeof(MSp
an*)); |
| 42 » » » runtime·SysFree(h->allspans, h->nspancap*sizeof(MSpan*))
; |
| 43 » » } |
| 44 » » h->allspans = all; |
| 45 » » h->nspancap = cap; |
| 46 » } |
| 47 » h->allspans[h->nspan++] = s; |
39 } | 48 } |
40 | 49 |
41 // Initialize the heap; fetch memory using alloc. | 50 // Initialize the heap; fetch memory using alloc. |
42 void | 51 void |
43 runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) | 52 runtime·MHeap_Init(MHeap *h, void *(*alloc)(uintptr)) |
44 { | 53 { |
45 uint32 i; | 54 uint32 i; |
46 | 55 |
47 runtime·FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h
); | 56 runtime·FixAlloc_Init(&h->spanalloc, sizeof(MSpan), alloc, RecordSpan, h
); |
48 runtime·FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil); | 57 runtime·FixAlloc_Init(&h->cachealloc, sizeof(MCache), alloc, nil, nil); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 | 109 |
101 HaveSpan: | 110 HaveSpan: |
102 // Mark span in use. | 111 // Mark span in use. |
103 if(s->state != MSpanFree) | 112 if(s->state != MSpanFree) |
104 runtime·throw("MHeap_AllocLocked - MSpan not free"); | 113 runtime·throw("MHeap_AllocLocked - MSpan not free"); |
105 if(s->npages < npage) | 114 if(s->npages < npage) |
106 runtime·throw("MHeap_AllocLocked - bad npages"); | 115 runtime·throw("MHeap_AllocLocked - bad npages"); |
107 runtime·MSpanList_Remove(s); | 116 runtime·MSpanList_Remove(s); |
108 s->state = MSpanInUse; | 117 s->state = MSpanInUse; |
109 mstats.heap_idle -= s->npages<<PageShift; | 118 mstats.heap_idle -= s->npages<<PageShift; |
| 119 mstats.heap_released -= s->npreleased<<PageShift; |
| 120 s->npreleased = 0; |
110 | 121 |
111 if(s->npages > npage) { | 122 if(s->npages > npage) { |
112 // Trim extra and put it back in the heap. | 123 // Trim extra and put it back in the heap. |
113 t = runtime·FixAlloc_Alloc(&h->spanalloc); | 124 t = runtime·FixAlloc_Alloc(&h->spanalloc); |
114 mstats.mspan_inuse = h->spanalloc.inuse; | 125 mstats.mspan_inuse = h->spanalloc.inuse; |
115 mstats.mspan_sys = h->spanalloc.sys; | 126 mstats.mspan_sys = h->spanalloc.sys; |
116 runtime·MSpan_Init(t, s->start + npage, s->npages - npage); | 127 runtime·MSpan_Init(t, s->start + npage, s->npages - npage); |
117 s->npages = npage; | 128 s->npages = npage; |
118 p = t->start; | 129 p = t->start; |
119 if(sizeof(void*) == 8) | 130 if(sizeof(void*) == 8) |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 uintptr *sp, *tp; | 288 uintptr *sp, *tp; |
278 MSpan *t; | 289 MSpan *t; |
279 PageID p; | 290 PageID p; |
280 | 291 |
281 if(s->state != MSpanInUse || s->ref != 0) { | 292 if(s->state != MSpanInUse || s->ref != 0) { |
282 runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %
d\n", s, s->start<<PageShift, s->state, s->ref); | 293 runtime·printf("MHeap_FreeLocked - span %p ptr %p state %d ref %
d\n", s, s->start<<PageShift, s->state, s->ref); |
283 runtime·throw("MHeap_FreeLocked - invalid free"); | 294 runtime·throw("MHeap_FreeLocked - invalid free"); |
284 } | 295 } |
285 mstats.heap_idle += s->npages<<PageShift; | 296 mstats.heap_idle += s->npages<<PageShift; |
286 s->state = MSpanFree; | 297 s->state = MSpanFree; |
| 298 s->unusedsince = 0; |
| 299 s->npreleased = 0; |
287 runtime·MSpanList_Remove(s); | 300 runtime·MSpanList_Remove(s); |
288 sp = (uintptr*)(s->start<<PageShift); | 301 sp = (uintptr*)(s->start<<PageShift); |
289 | 302 |
290 // Coalesce with earlier, later spans. | 303 // Coalesce with earlier, later spans. |
291 p = s->start; | 304 p = s->start; |
292 if(sizeof(void*) == 8) | 305 if(sizeof(void*) == 8) |
293 p -= (uintptr)h->arena_start >> PageShift; | 306 p -= (uintptr)h->arena_start >> PageShift; |
294 if(p > 0 && (t = h->map[p-1]) != nil && t->state != MSpanInUse) { | 307 if(p > 0 && (t = h->map[p-1]) != nil && t->state != MSpanInUse) { |
295 tp = (uintptr*)(t->start<<PageShift); | 308 tp = (uintptr*)(t->start<<PageShift); |
296 *tp |= *sp; // propagate "needs zeroing" mark | 309 *tp |= *sp; // propagate "needs zeroing" mark |
297 s->start = t->start; | 310 s->start = t->start; |
298 s->npages += t->npages; | 311 s->npages += t->npages; |
| 312 s->npreleased = t->npreleased; // absorb released pages |
299 p -= t->npages; | 313 p -= t->npages; |
300 h->map[p] = s; | 314 h->map[p] = s; |
301 runtime·MSpanList_Remove(t); | 315 runtime·MSpanList_Remove(t); |
302 t->state = MSpanDead; | 316 t->state = MSpanDead; |
303 runtime·FixAlloc_Free(&h->spanalloc, t); | 317 runtime·FixAlloc_Free(&h->spanalloc, t); |
304 mstats.mspan_inuse = h->spanalloc.inuse; | 318 mstats.mspan_inuse = h->spanalloc.inuse; |
305 mstats.mspan_sys = h->spanalloc.sys; | 319 mstats.mspan_sys = h->spanalloc.sys; |
306 } | 320 } |
307 if(p+s->npages < nelem(h->map) && (t = h->map[p+s->npages]) != nil && t-
>state != MSpanInUse) { | 321 if(p+s->npages < nelem(h->map) && (t = h->map[p+s->npages]) != nil && t-
>state != MSpanInUse) { |
308 tp = (uintptr*)(t->start<<PageShift); | 322 tp = (uintptr*)(t->start<<PageShift); |
309 *sp |= *tp; // propagate "needs zeroing" mark | 323 *sp |= *tp; // propagate "needs zeroing" mark |
310 s->npages += t->npages; | 324 s->npages += t->npages; |
| 325 s->npreleased += t->npreleased; |
311 h->map[p + s->npages - 1] = s; | 326 h->map[p + s->npages - 1] = s; |
312 runtime·MSpanList_Remove(t); | 327 runtime·MSpanList_Remove(t); |
313 t->state = MSpanDead; | 328 t->state = MSpanDead; |
314 runtime·FixAlloc_Free(&h->spanalloc, t); | 329 runtime·FixAlloc_Free(&h->spanalloc, t); |
315 mstats.mspan_inuse = h->spanalloc.inuse; | 330 mstats.mspan_inuse = h->spanalloc.inuse; |
316 mstats.mspan_sys = h->spanalloc.sys; | 331 mstats.mspan_sys = h->spanalloc.sys; |
317 } | 332 } |
318 | 333 |
319 // Insert s into appropriate list. | 334 // Insert s into appropriate list. |
320 if(s->npages < nelem(h->free)) | 335 if(s->npages < nelem(h->free)) |
321 runtime·MSpanList_Insert(&h->free[s->npages], s); | 336 runtime·MSpanList_Insert(&h->free[s->npages], s); |
322 else | 337 else |
323 runtime·MSpanList_Insert(&h->large, s); | 338 runtime·MSpanList_Insert(&h->large, s); |
324 | 339 } |
325 » // TODO(rsc): IncrementalScavenge() to return memory to OS. | 340 |
| 341 // Release (part of) unused memory to OS. |
| 342 // Goroutine created at startup. |
| 343 // Loop forever. |
| 344 void |
| 345 runtime·MHeap_Scavenger(void) |
| 346 { |
| 347 » MHeap *h; |
| 348 » MSpan *s, *list; |
| 349 » uint64 tick, now, forcegc, limit; |
| 350 » uint32 k, i; |
| 351 » uintptr released, sumreleased; |
| 352 » byte *env; |
| 353 » bool trace; |
| 354 » Note note; |
| 355 |
| 356 » // If we go two minutes without a garbage collection, force one to run. |
| 357 » forcegc = 2*60*1e9; |
| 358 » // If a span goes unused for 5 minutes after a garbage collection, |
| 359 » // we hand it back to the operating system. |
| 360 » limit = 5*60*1e9; |
| 361 » // Make wake-up period small enough for the sampling to be correct. |
| 362 » if(forcegc < limit) |
| 363 » » tick = forcegc/2; |
| 364 » else |
| 365 » » tick = limit/2; |
| 366 |
| 367 » trace = false; |
| 368 » env = runtime·getenv("GOGCTRACE"); |
| 369 » if(env != nil) |
| 370 » » trace = runtime·atoi(env) > 0; |
| 371 |
| 372 » h = &runtime·mheap; |
| 373 » for(k=0;; k++) { |
| 374 » » runtime·noteclear(¬e); |
| 375 » » runtime·entersyscall(); |
| 376 » » runtime·notetsleep(¬e, tick); |
| 377 » » runtime·exitsyscall(); |
| 378 |
| 379 » » runtime·lock(h); |
| 380 » » now = runtime·nanotime(); |
| 381 » » if(now - mstats.last_gc > forcegc) { |
| 382 » » » runtime·unlock(h); |
| 383 » » » runtime·gc(1); |
| 384 » » » runtime·lock(h); |
| 385 » » » now = runtime·nanotime(); |
| 386 » » » if (trace) |
| 387 » » » » runtime·printf("scvg%d: GC forced\n", k); |
| 388 » » } |
| 389 » » sumreleased = 0; |
| 390 » » for(i=0; i < nelem(h->free)+1; i++) { |
| 391 » » » if(i < nelem(h->free)) |
| 392 » » » » list = &h->free[i]; |
| 393 » » » else |
| 394 » » » » list = &h->large; |
| 395 » » » if(runtime·MSpanList_IsEmpty(list)) |
| 396 » » » » continue; |
| 397 » » » for(s=list->next; s != list; s=s->next) { |
| 398 » » » » if(s->unusedsince != 0 && (now - s->unusedsince)
> limit) { |
| 399 » » » » » released = (s->npages - s->npreleased) <
< PageShift; |
| 400 » » » » » mstats.heap_released += released; |
| 401 » » » » » sumreleased += released; |
| 402 » » » » » s->npreleased = s->npages; |
| 403 » » » » » runtime·SysUnused((void*)(s->start << Pa
geShift), s->npages << PageShift); |
| 404 » » » » } |
| 405 » » » } |
| 406 » » } |
| 407 » » runtime·unlock(h); |
| 408 |
| 409 » » if(trace) { |
| 410 » » » if(sumreleased > 0) |
| 411 » » » » runtime·printf("scvg%d: %p MB released\n", k, su
mreleased>>20); |
| 412 » » » runtime·printf("scvg%d: inuse: %D, idle: %D, sys: %D, re
leased: %D, consumed: %D (MB)\n", |
| 413 » » » » k, mstats.heap_inuse>>20, mstats.heap_idle>>20,
mstats.heap_sys>>20, |
| 414 » » » » mstats.heap_released>>20, (mstats.heap_sys - mst
ats.heap_released)>>20); |
| 415 » » } |
| 416 » } |
326 } | 417 } |
327 | 418 |
328 // Initialize a new span with the given start and npages. | 419 // Initialize a new span with the given start and npages. |
329 void | 420 void |
330 runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages) | 421 runtime·MSpan_Init(MSpan *span, PageID start, uintptr npages) |
331 { | 422 { |
332 span->next = nil; | 423 span->next = nil; |
333 span->prev = nil; | 424 span->prev = nil; |
334 span->start = start; | 425 span->start = start; |
335 span->npages = npages; | 426 span->npages = npages; |
336 span->freelist = nil; | 427 span->freelist = nil; |
337 span->ref = 0; | 428 span->ref = 0; |
338 span->sizeclass = 0; | 429 span->sizeclass = 0; |
339 span->state = 0; | 430 span->state = 0; |
| 431 span->unusedsince = 0; |
| 432 span->npreleased = 0; |
340 } | 433 } |
341 | 434 |
342 // Initialize an empty doubly-linked list. | 435 // Initialize an empty doubly-linked list. |
343 void | 436 void |
344 runtime·MSpanList_Init(MSpan *list) | 437 runtime·MSpanList_Init(MSpan *list) |
345 { | 438 { |
346 list->state = MSpanListHead; | 439 list->state = MSpanListHead; |
347 list->next = list; | 440 list->next = list; |
348 list->prev = list; | 441 list->prev = list; |
349 } | 442 } |
(...skipping 22 matching lines...) Expand all Loading... |
372 runtime·printf("failed MSpanList_Insert %p %p %p\n", span, span-
>next, span->prev); | 465 runtime·printf("failed MSpanList_Insert %p %p %p\n", span, span-
>next, span->prev); |
373 runtime·throw("MSpanList_Insert"); | 466 runtime·throw("MSpanList_Insert"); |
374 } | 467 } |
375 span->next = list->next; | 468 span->next = list->next; |
376 span->prev = list; | 469 span->prev = list; |
377 span->next->prev = span; | 470 span->next->prev = span; |
378 span->prev->next = span; | 471 span->prev->next = span; |
379 } | 472 } |
380 | 473 |
381 | 474 |
LEFT | RIGHT |