2014-11-11 15:05:02 -07:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
// TODO(rsc): The code having to do with the heap bitmap needs very serious cleanup.
|
|
|
|
// It has gotten completely out of control.
|
|
|
|
|
|
|
|
// Garbage collector (GC).
|
|
|
|
//
|
2014-12-09 11:25:45 -07:00
|
|
|
// The GC runs concurrently with mutator threads, is type accurate (aka precise), allows multiple
|
|
|
|
// GC thread to run in parallel. It is a concurrent mark and sweep that uses a write barrier. It is
|
2014-11-15 06:00:38 -07:00
|
|
|
// non-generational and non-compacting. Allocation is done using size segregated per P allocation
|
|
|
|
// areas to minimize fragmentation while eliminating locks in the common case.
|
2014-11-11 15:05:02 -07:00
|
|
|
//
|
2014-11-15 06:00:38 -07:00
|
|
|
// The algorithm decomposes into several steps.
|
|
|
|
// This is a high level description of the algorithm being used. For an overview of GC a good
|
|
|
|
// place to start is Richard Jones' gchandbook.org.
|
|
|
|
//
|
|
|
|
// The algorithm's intellectual heritage includes Dijkstra's on-the-fly algorithm, see
|
|
|
|
// Edsger W. Dijkstra, Leslie Lamport, A. J. Martin, C. S. Scholten, and E. F. M. Steffens. 1978.
|
2014-12-09 08:15:18 -07:00
|
|
|
// On-the-fly garbage collection: an exercise in cooperation. Commun. ACM 21, 11 (November 1978),
|
2014-12-09 11:25:45 -07:00
|
|
|
// 966-975.
|
2014-11-15 06:00:38 -07:00
|
|
|
// For journal quality proofs that these steps are complete, correct, and terminate see
|
|
|
|
// Hudson, R., and Moss, J.E.B. Copying Garbage Collection without stopping the world.
|
|
|
|
// Concurrency and Computation: Practice and Experience 15(3-5), 2003.
|
2014-11-11 15:05:02 -07:00
|
|
|
//
|
2014-11-15 06:00:38 -07:00
|
|
|
// 0. Set phase = GCscan from GCoff.
|
|
|
|
// 1. Wait for all P's to acknowledge phase change.
|
|
|
|
// At this point all goroutines have passed through a GC safepoint and
|
|
|
|
// know we are in the GCscan phase.
|
|
|
|
// 2. GC scans all goroutine stacks, mark and enqueues all encountered pointers
|
2014-12-09 11:25:45 -07:00
|
|
|
// (marking avoids most duplicate enqueuing but races may produce benign duplication).
|
2014-11-15 06:00:38 -07:00
|
|
|
// Preempted goroutines are scanned before P schedules next goroutine.
|
|
|
|
// 3. Set phase = GCmark.
|
|
|
|
// 4. Wait for all P's to acknowledge phase change.
|
|
|
|
// 5. Now write barrier marks and enqueues black, grey, or white to white pointers.
|
|
|
|
// Malloc still allocates white (non-marked) objects.
|
|
|
|
// 6. Meanwhile GC transitively walks the heap marking reachable objects.
|
|
|
|
// 7. When GC finishes marking heap, it preempts P's one-by-one and
|
|
|
|
// retakes partial wbufs (filled by write barrier or during a stack scan of the goroutine
|
|
|
|
// currently scheduled on the P).
|
|
|
|
// 8. Once the GC has exhausted all available marking work it sets phase = marktermination.
|
|
|
|
// 9. Wait for all P's to acknowledge phase change.
|
|
|
|
// 10. Malloc now allocates black objects, so number of unmarked reachable objects
|
|
|
|
// monotonically decreases.
|
2014-12-09 08:15:18 -07:00
|
|
|
// 11. GC preempts P's one-by-one taking partial wbufs and marks all unmarked yet
|
2014-12-09 11:25:45 -07:00
|
|
|
// reachable objects.
|
2014-11-15 06:00:38 -07:00
|
|
|
// 12. When GC completes a full cycle over P's and discovers no new grey
|
|
|
|
// objects, (which means all reachable objects are marked) set phase = GCsweep.
|
|
|
|
// 13. Wait for all P's to acknowledge phase change.
|
|
|
|
// 14. Now malloc allocates white (but sweeps spans before use).
|
|
|
|
// Write barrier becomes nop.
|
|
|
|
// 15. GC does background sweeping, see description below.
|
|
|
|
// 16. When sweeping is complete set phase to GCoff.
|
|
|
|
// 17. When sufficient allocation has taken place replay the sequence starting at 0 above,
|
|
|
|
// see discussion of GC rate below.
|
|
|
|
|
|
|
|
// Changing phases.
|
|
|
|
// Phases are changed by setting the gcphase to the next phase and possibly calling ackgcphase.
|
|
|
|
// All phase action must be benign in the presence of a change.
|
|
|
|
// Starting with GCoff
|
|
|
|
// GCoff to GCscan
|
|
|
|
// GSscan scans stacks and globals greying them and never marks an object black.
|
|
|
|
// Once all the P's are aware of the new phase they will scan gs on preemption.
|
|
|
|
// This means that the scanning of preempted gs can't start until all the Ps
|
|
|
|
// have acknowledged.
|
|
|
|
// GCscan to GCmark
|
|
|
|
// GCMark turns on the write barrier which also only greys objects. No scanning
|
|
|
|
// of objects (making them black) can happen until all the Ps have acknowledged
|
|
|
|
// the phase change.
|
|
|
|
// GCmark to GCmarktermination
|
|
|
|
// The only change here is that we start allocating black so the Ps must acknowledge
|
|
|
|
// the change before we begin the termination algorithm
|
|
|
|
// GCmarktermination to GSsweep
|
|
|
|
// Object currently on the freelist must be marked black for this to work.
|
|
|
|
// Are things on the free lists black or white? How does the sweep phase work?
|
|
|
|
|
2014-11-11 15:05:02 -07:00
|
|
|
// Concurrent sweep.
|
|
|
|
// The sweep phase proceeds concurrently with normal program execution.
|
|
|
|
// The heap is swept span-by-span both lazily (when a goroutine needs another span)
|
|
|
|
// and concurrently in a background goroutine (this helps programs that are not CPU bound).
|
|
|
|
// However, at the end of the stop-the-world GC phase we don't know the size of the live heap,
|
|
|
|
// and so next_gc calculation is tricky and happens as follows.
|
|
|
|
// At the end of the stop-the-world phase next_gc is conservatively set based on total
|
|
|
|
// heap size; all spans are marked as "needs sweeping".
|
|
|
|
// Whenever a span is swept, next_gc is decremented by GOGC*newly_freed_memory.
|
|
|
|
// The background sweeper goroutine simply sweeps spans one-by-one bringing next_gc
|
|
|
|
// closer to the target value. However, this is not enough to avoid over-allocating memory.
|
|
|
|
// Consider that a goroutine wants to allocate a new span for a large object and
|
|
|
|
// there are no free swept spans, but there are small-object unswept spans.
|
|
|
|
// If the goroutine naively allocates a new span, it can surpass the yet-unknown
|
|
|
|
// target next_gc value. In order to prevent such cases (1) when a goroutine needs
|
|
|
|
// to allocate a new small-object span, it sweeps small-object spans for the same
|
|
|
|
// object size until it frees at least one object; (2) when a goroutine needs to
|
|
|
|
// allocate large-object span from heap, it sweeps spans until it frees at least
|
|
|
|
// that many pages into heap. Together these two measures ensure that we don't surpass
|
|
|
|
// target next_gc value by a large margin. There is an exception: if a goroutine sweeps
|
|
|
|
// and frees two nonadjacent one-page spans to the heap, it will allocate a new two-page span,
|
2014-12-09 08:15:18 -07:00
|
|
|
// but there can still be other one-page unswept spans which could be combined into a
|
2014-12-09 11:25:45 -07:00
|
|
|
// two-page span.
|
2014-11-11 15:05:02 -07:00
|
|
|
// It's critical to ensure that no operations proceed on unswept spans (that would corrupt
|
|
|
|
// mark bits in GC bitmap). During GC all mcaches are flushed into the central cache,
|
|
|
|
// so they are empty. When a goroutine grabs a new span into mcache, it sweeps it.
|
|
|
|
// When a goroutine explicitly frees an object or sets a finalizer, it ensures that
|
|
|
|
// the span is swept (either by sweeping it, or by waiting for the concurrent sweep to finish).
|
|
|
|
// The finalizer goroutine is kicked off only when all spans are swept.
|
|
|
|
// When the next GC starts, it sweeps all not-yet-swept spans (if any).
|
|
|
|
|
2014-11-15 06:00:38 -07:00
|
|
|
// GC rate.
|
|
|
|
// Next GC is after we've allocated an extra amount of memory proportional to
|
|
|
|
// the amount already in use. The proportion is controlled by GOGC environment variable
|
|
|
|
// (100 by default). If GOGC=100 and we're using 4M, we'll GC again when we get to 8M
|
|
|
|
// (this mark is tracked in next_gc variable). This keeps the GC cost in linear
|
|
|
|
// proportion to the allocation cost. Adjusting GOGC just changes the linear constant
|
|
|
|
// (and also the amount of extra memory used).
|
|
|
|
|
2014-11-11 15:05:02 -07:00
|
|
|
package runtime
|
|
|
|
|
|
|
|
import "unsafe"
|
|
|
|
|
|
|
|
const (
|
|
|
|
_DebugGC = 0
|
|
|
|
_ConcurrentSweep = true
|
|
|
|
_FinBlockSize = 4 * 1024
|
|
|
|
_RootData = 0
|
|
|
|
_RootBss = 1
|
|
|
|
_RootFinalizers = 2
|
|
|
|
_RootSpans = 3
|
|
|
|
_RootFlushCaches = 4
|
|
|
|
_RootCount = 5
|
|
|
|
)
|
|
|
|
|
|
|
|
//go:linkname weak_cgo_allocate go.weak.runtime._cgo_allocate_internal
|
|
|
|
var weak_cgo_allocate byte
|
|
|
|
|
|
|
|
// Is _cgo_allocate linked into the binary?
|
2014-12-09 08:15:18 -07:00
|
|
|
//go:nowritebarrier
|
2014-11-11 15:05:02 -07:00
|
|
|
func have_cgo_allocate() bool {
|
|
|
|
return &weak_cgo_allocate != nil
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
var gcdatamask bitvector
|
|
|
|
var gcbssmask bitvector
|
runtime: fix two garbage collector bugs
First, call clearcheckmarks immediately after changing checkmark,
so that there is less time when the checkmark flag and the bitmap
are inconsistent. The tiny gap between the two lines is fine, because
the world is stopped. Before, the gap was much larger and included
such code as "go bgsweep()", which allocated.
Second, modify gcphase only when the world is stopped.
As written, gcscan_m was changing gcphase from 0 to GCscan
and back to 0 while other goroutines were running.
Another goroutine running at the same time might decide to
sleep, see GCscan, call gcphasework, and start "helping" by
scanning its stack. That's fine, except that if gcphase flips back
to 0 as the goroutine calls scanblock, it will start draining the
work buffers prematurely.
Both of these were found wbshadow=2 (and a lot of hard work).
Eventually that will run automatically, but right now it still
doesn't quite work for all.bash, due to mmap conflicts with
pthread-created threads.
Change-Id: I99aa8210cff9c6e7d0a1b62c75be32a23321897b
Reviewed-on: https://go-review.googlesource.com/2340
Reviewed-by: Rick Hudson <rlh@golang.org>
2015-01-05 13:02:09 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// heapminimum is the minimum number of bytes in the heap.
|
|
|
|
// This cleans up the corner case of where we have a very small live set but a lot
|
|
|
|
// of allocations and collecting every GOGC * live set is expensive.
|
|
|
|
var heapminimum = uint64(4 << 20)
|
2014-11-15 06:00:38 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Initialized from $GOGC. GOGC=off means no GC.
|
|
|
|
var gcpercent int32
|
2015-01-06 12:58:49 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
func gcinit() {
|
|
|
|
if unsafe.Sizeof(workbuf{}) != _WorkbufSize {
|
|
|
|
throw("size of Workbuf is suboptimal")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
runtime: switch to gcWork abstraction
This converts the garbage collector from directly manipulating work
buffers to using the new gcWork abstraction.
The previous management of work buffers was rather ad hoc. As a
result, switching to the gcWork abstraction changes many details of
work buffer management.
If greyobject fills a work buffer, it can now pull from work.partial
in addition to work.empty.
Previously, gcDrain started with a partial or empty work buffer and
fetched an empty work buffer if it filled its current buffer (in
greyobject). Now, gcDrain starts with a full work buffer and fetches
an partial or empty work buffer if it fills its current buffer (in
greyobject). The original behavior was bad because gcDrain would
immediately drop the empty work buffer returned by greyobject and
fetch a full work buffer, which greyobject was likely to immediately
overflow, fetching another empty work buffer, etc. The new behavior
isn't great at the start because greyobject is likely to immediately
overflow the full buffer, but the steady-state behavior should be more
stable. Both before and after this change, gcDrain fetches a full
work buffer if it drains its current buffer. Basically all of these
choices are bad; the right answer is to use a dual work buffer scheme.
Previously, shade always fetched a work buffer (though usually from
m.currentwbuf), even if the object was already marked. Now it only
fetches a work buffer if it actually greys an object.
Change-Id: I8b880ed660eb63135236fa5d5678f0c1c041881f
Reviewed-on: https://go-review.googlesource.com/5232
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
2015-02-17 08:53:31 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
work.markfor = parforalloc(_MaxGcproc)
|
|
|
|
gcpercent = readgogc()
|
2015-03-11 17:22:18 -06:00
|
|
|
gcdatamask = unrollglobgcprog((*byte)(unsafe.Pointer(themoduledata.gcdata)), themoduledata.edata-themoduledata.data)
|
|
|
|
gcbssmask = unrollglobgcprog((*byte)(unsafe.Pointer(themoduledata.gcbss)), themoduledata.ebss-themoduledata.bss)
|
2015-02-19 11:38:46 -07:00
|
|
|
memstats.next_gc = heapminimum
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-03-05 14:04:17 -07:00
|
|
|
// gcenable is called after the bulk of the runtime initialization,
|
|
|
|
// just before we're about to start letting user code run.
|
|
|
|
// It kicks off the background sweeper goroutine and enables GC.
|
|
|
|
func gcenable() {
|
|
|
|
c := make(chan int, 1)
|
|
|
|
go bgsweep(c)
|
|
|
|
<-c
|
|
|
|
memstats.enablegc = true // now that runtime is initialized, GC is okay
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
func setGCPercent(in int32) (out int32) {
|
|
|
|
lock(&mheap_.lock)
|
|
|
|
out = gcpercent
|
|
|
|
if in < 0 {
|
|
|
|
in = -1
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-02-19 11:38:46 -07:00
|
|
|
gcpercent = in
|
|
|
|
unlock(&mheap_.lock)
|
|
|
|
return out
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Trigger the concurrent GC when 1/triggerratio memory is available to allocate.
|
|
|
|
// Adjust this ratio as part of a scheme to ensure that mutators have enough
|
|
|
|
// memory to allocate in durring a concurrent GC cycle.
|
|
|
|
var triggerratio = int64(8)
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Determine whether to initiate a GC.
|
|
|
|
// If the GC is already working no need to trigger another one.
|
|
|
|
// This should establish a feedback loop where if the GC does not
|
|
|
|
// have sufficient time to complete then more memory will be
|
|
|
|
// requested from the OS increasing heap size thus allow future
|
|
|
|
// GCs more time to complete.
|
|
|
|
// memstat.heap_alloc and memstat.next_gc reads have benign races
|
|
|
|
// A false negative simple does not start a GC, a false positive
|
|
|
|
// will start a GC needlessly. Neither have correctness issues.
|
|
|
|
func shouldtriggergc() bool {
|
|
|
|
return triggerratio*(int64(memstats.next_gc)-int64(memstats.heap_alloc)) <= int64(memstats.next_gc) && atomicloaduint(&bggc.working) == 0
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
var work struct {
|
2015-02-19 11:38:46 -07:00
|
|
|
full uint64 // lock-free list of full blocks workbuf
|
|
|
|
empty uint64 // lock-free list of empty blocks workbuf
|
|
|
|
partial uint64 // lock-free list of partially filled blocks workbuf
|
|
|
|
pad0 [_CacheLineSize]uint8 // prevents false-sharing between full/empty and nproc/nwait
|
|
|
|
nproc uint32
|
|
|
|
tstart int64
|
|
|
|
nwait uint32
|
|
|
|
ndone uint32
|
|
|
|
alldone note
|
|
|
|
markfor *parfor
|
2014-11-15 06:00:38 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Copy of mheap.allspans for marker or sweeper.
|
|
|
|
spans []*mspan
|
2015-01-06 12:58:49 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// GC runs a garbage collection.
|
|
|
|
func GC() {
|
2015-02-19 13:48:40 -07:00
|
|
|
startGC(gcForceBlockMode)
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
const (
|
|
|
|
gcBackgroundMode = iota // concurrent GC
|
|
|
|
gcForceMode // stop-the-world GC now
|
|
|
|
gcForceBlockMode // stop-the-world GC now and wait for sweep
|
|
|
|
)
|
|
|
|
|
|
|
|
func startGC(mode int) {
|
2015-02-19 11:38:46 -07:00
|
|
|
// 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 deadlocks during stoptheworld, don't bother
|
|
|
|
// trying to run gc while holding a lock. The next mallocgc without a lock
|
|
|
|
// will do the gc instead.
|
|
|
|
mp := acquirem()
|
|
|
|
if gp := getg(); gp == mp.g0 || mp.locks > 1 || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
|
|
|
|
releasem(mp)
|
2014-11-11 15:05:02 -07:00
|
|
|
return
|
|
|
|
}
|
2015-02-19 11:38:46 -07:00
|
|
|
releasem(mp)
|
|
|
|
mp = nil
|
2014-11-20 10:08:13 -07:00
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
if mode != gcBackgroundMode {
|
|
|
|
// special synchronous cases
|
|
|
|
gc(mode)
|
|
|
|
return
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-02-19 13:48:40 -07:00
|
|
|
|
|
|
|
// trigger concurrent GC
|
|
|
|
lock(&bggc.lock)
|
|
|
|
if !bggc.started {
|
|
|
|
bggc.working = 1
|
|
|
|
bggc.started = true
|
|
|
|
go backgroundgc()
|
|
|
|
} else if bggc.working == 0 {
|
|
|
|
bggc.working = 1
|
2015-02-21 11:01:40 -07:00
|
|
|
ready(bggc.g, 0)
|
2015-02-19 13:48:40 -07:00
|
|
|
}
|
|
|
|
unlock(&bggc.lock)
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-01-06 12:58:49 -07:00
|
|
|
// State of the background concurrent GC goroutine.
|
|
|
|
var bggc struct {
|
|
|
|
lock mutex
|
|
|
|
g *g
|
|
|
|
working uint
|
|
|
|
started bool
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// backgroundgc is running in a goroutine and does the concurrent GC work.
|
|
|
|
// bggc holds the state of the backgroundgc.
|
|
|
|
func backgroundgc() {
|
|
|
|
bggc.g = getg()
|
2014-11-11 15:05:02 -07:00
|
|
|
for {
|
2015-02-19 13:48:40 -07:00
|
|
|
gc(gcBackgroundMode)
|
2015-02-19 11:38:46 -07:00
|
|
|
lock(&bggc.lock)
|
|
|
|
bggc.working = 0
|
2015-02-21 11:01:40 -07:00
|
|
|
goparkunlock(&bggc.lock, "Concurrent GC wait", traceEvGoBlock, 1)
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
func gc(mode int) {
|
2015-03-26 16:48:42 -06:00
|
|
|
// debug.gctrace variables
|
|
|
|
var stwprocs, maxprocs int32
|
|
|
|
var tSweepTerm, tScan, tInstallWB, tMark, tMarkTerm int64
|
|
|
|
var heap0, heap1 uint64
|
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
// Ok, we're doing it! Stop everybody else
|
2015-02-19 11:38:46 -07:00
|
|
|
semacquire(&worldsema, false)
|
2014-12-12 10:41:57 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Pick up the remaining unswept/not being swept spans concurrently
|
|
|
|
for gosweepone() != ^uintptr(0) {
|
|
|
|
sweep.nbgsweep++
|
2014-11-15 06:00:38 -07:00
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
mp := acquirem()
|
|
|
|
mp.preemptoff = "gcing"
|
|
|
|
releasem(mp)
|
|
|
|
gctimer.count++
|
2015-02-19 13:48:40 -07:00
|
|
|
if mode == gcBackgroundMode {
|
2015-02-19 11:38:46 -07:00
|
|
|
gctimer.cycle.sweepterm = nanotime()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
stwprocs, maxprocs = gcprocs(), gomaxprocs
|
|
|
|
tSweepTerm = nanotime()
|
|
|
|
heap0 = memstats.heap_alloc
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceGoSched()
|
|
|
|
traceGCStart()
|
|
|
|
}
|
|
|
|
|
|
|
|
systemstack(stoptheworld)
|
|
|
|
systemstack(finishsweep_m) // finish sweep before we start concurrent scan.
|
2015-03-05 15:33:08 -07:00
|
|
|
// clearpools before we start the GC. If we wait they memory will not be
|
|
|
|
// reclaimed until the next GC cycle.
|
|
|
|
clearpools()
|
2015-02-19 13:48:40 -07:00
|
|
|
|
|
|
|
if mode == gcBackgroundMode { // Do as much work concurrently as possible
|
|
|
|
systemstack(func() {
|
|
|
|
gcphase = _GCscan
|
|
|
|
|
|
|
|
// Concurrent scan.
|
|
|
|
starttheworld()
|
|
|
|
gctimer.cycle.scan = nanotime()
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
tScan = nanotime()
|
|
|
|
}
|
2015-02-19 13:48:40 -07:00
|
|
|
gcscan_m()
|
|
|
|
gctimer.cycle.installmarkwb = nanotime()
|
|
|
|
|
2015-03-17 13:31:11 -06:00
|
|
|
// Enter mark phase and enable write barriers.
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
tInstallWB = nanotime()
|
|
|
|
}
|
2015-02-19 13:48:40 -07:00
|
|
|
stoptheworld()
|
|
|
|
gcphase = _GCmark
|
|
|
|
|
|
|
|
// Concurrent mark.
|
|
|
|
starttheworld()
|
|
|
|
})
|
2015-03-18 09:22:12 -06:00
|
|
|
gctimer.cycle.mark = nanotime()
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
tMark = nanotime()
|
|
|
|
}
|
2015-03-18 09:22:12 -06:00
|
|
|
var gcw gcWork
|
|
|
|
gcDrain(&gcw)
|
|
|
|
gcw.dispose()
|
2015-03-19 12:37:30 -06:00
|
|
|
// Despite the barrier in gcDrain, gcDrainNs may still
|
|
|
|
// be doing work at this point. This is okay because
|
|
|
|
// 1) the gcDrainNs happen on the system stack, so
|
|
|
|
// they will flush their work to the global queues
|
|
|
|
// before we can stop the world, and 2) it's fine if
|
|
|
|
// we go into mark termination with some work queued.
|
2015-03-18 09:22:12 -06:00
|
|
|
|
|
|
|
// Begin mark termination.
|
|
|
|
gctimer.cycle.markterm = nanotime()
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
tMarkTerm = nanotime()
|
|
|
|
}
|
2015-03-18 09:22:12 -06:00
|
|
|
systemstack(stoptheworld)
|
|
|
|
// The gcphase is _GCmark, it will transition to _GCmarktermination
|
|
|
|
// below. The important thing is that the wb remains active until
|
|
|
|
// all marking is complete. This includes writes made by the GC.
|
2015-02-19 11:38:46 -07:00
|
|
|
} else {
|
2015-02-19 13:48:40 -07:00
|
|
|
// For non-concurrent GC (mode != gcBackgroundMode)
|
2015-02-24 20:20:38 -07:00
|
|
|
// The g stacks have not been scanned so clear g state
|
2015-02-19 13:48:40 -07:00
|
|
|
// such that mark termination scans all stacks.
|
2015-02-24 20:20:38 -07:00
|
|
|
gcResetGState()
|
2015-03-26 16:48:42 -06:00
|
|
|
|
|
|
|
if debug.gctrace > 0 {
|
|
|
|
t := nanotime()
|
|
|
|
tScan, tInstallWB, tMark, tMarkTerm = t, t, t, t
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-03-05 15:33:08 -07:00
|
|
|
// World is stopped.
|
|
|
|
// Start marktermination which includes enabling the write barrier.
|
|
|
|
gcphase = _GCmarktermination
|
|
|
|
|
2015-03-26 16:48:42 -06:00
|
|
|
if debug.gctrace > 0 {
|
|
|
|
heap1 = memstats.heap_alloc
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
startTime := nanotime()
|
|
|
|
if mp != acquirem() {
|
2015-02-19 13:48:40 -07:00
|
|
|
throw("gcwork: rescheduled")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
_g_ := getg()
|
|
|
|
_g_.m.traceback = 2
|
|
|
|
gp := _g_.m.curg
|
|
|
|
casgstatus(gp, _Grunning, _Gwaiting)
|
|
|
|
gp.waitreason = "garbage collection"
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Run gc on the g0 stack. We do this so that the g stack
|
|
|
|
// we're currently running on will no longer change. Cuts
|
|
|
|
// the root set down a bit (g0 stacks are not scanned, and
|
|
|
|
// we don't need to scan gc's internal state). We also
|
|
|
|
// need to switch to g0 so we can shrink the stack.
|
2015-02-19 14:21:00 -07:00
|
|
|
systemstack(func() {
|
2015-02-19 14:43:27 -07:00
|
|
|
gcMark(startTime)
|
|
|
|
if debug.gccheckmark > 0 {
|
|
|
|
// Run a full stop-the-world mark using checkmark bits,
|
|
|
|
// to check that we didn't forget to mark anything during
|
|
|
|
// the concurrent mark process.
|
|
|
|
initCheckmarks()
|
|
|
|
gcMark(startTime)
|
|
|
|
clearCheckmarks()
|
2015-02-19 13:48:40 -07:00
|
|
|
}
|
2015-03-05 15:33:08 -07:00
|
|
|
|
|
|
|
// marking is complete so we can turn the write barrier off
|
|
|
|
gcphase = _GCoff
|
2015-02-19 14:43:27 -07:00
|
|
|
gcSweep(mode)
|
2015-02-19 13:48:40 -07:00
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
if debug.gctrace > 1 {
|
|
|
|
startTime = nanotime()
|
2015-02-24 15:49:55 -07:00
|
|
|
// The g stacks have been scanned so
|
|
|
|
// they have gcscanvalid==true and gcworkdone==true.
|
|
|
|
// Reset these so that all stacks will be rescanned.
|
2015-02-24 20:20:38 -07:00
|
|
|
gcResetGState()
|
2015-02-19 14:43:27 -07:00
|
|
|
finishsweep_m()
|
2015-03-05 15:33:08 -07:00
|
|
|
|
|
|
|
// Still in STW but gcphase is _GCoff, reset to _GCmarktermination
|
|
|
|
// At this point all objects will be found during the gcMark which
|
|
|
|
// does a complete STW mark and object scan.
|
|
|
|
gcphase = _GCmarktermination
|
2015-02-19 14:43:27 -07:00
|
|
|
gcMark(startTime)
|
2015-03-05 15:33:08 -07:00
|
|
|
gcphase = _GCoff // marking is done, turn off wb.
|
2015-02-19 14:43:27 -07:00
|
|
|
gcSweep(mode)
|
2015-02-19 13:48:40 -07:00
|
|
|
}
|
2015-02-19 11:38:46 -07:00
|
|
|
})
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
_g_.m.traceback = 0
|
|
|
|
casgstatus(gp, _Gwaiting, _Grunning)
|
2015-02-19 14:21:00 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceGCDone()
|
|
|
|
traceGoStart()
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// all done
|
|
|
|
mp.preemptoff = ""
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
if mode == gcBackgroundMode {
|
2015-02-19 11:38:46 -07:00
|
|
|
gctimer.cycle.sweep = nanotime()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
semrelease(&worldsema)
|
|
|
|
|
2015-02-19 13:48:40 -07:00
|
|
|
if mode == gcBackgroundMode {
|
2015-02-19 11:38:46 -07:00
|
|
|
if gctimer.verbose > 1 {
|
|
|
|
GCprinttimes()
|
|
|
|
} else if gctimer.verbose > 0 {
|
|
|
|
calctimes() // ignore result
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-03-05 15:33:08 -07:00
|
|
|
if gcphase != _GCoff {
|
|
|
|
throw("gc done but gcphase != _GCoff")
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
systemstack(starttheworld)
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
releasem(mp)
|
|
|
|
mp = nil
|
2015-01-28 13:57:46 -07:00
|
|
|
|
2015-03-26 16:48:42 -06:00
|
|
|
memstats.numgc++
|
|
|
|
if debug.gctrace > 0 {
|
|
|
|
// TODO(austin): Cumulative utilization %
|
|
|
|
// TODO(austin): Marked heap size at end
|
|
|
|
tEnd := nanotime()
|
|
|
|
var sbuf [24]byte
|
|
|
|
printlock()
|
|
|
|
print("gc #", memstats.numgc,
|
|
|
|
" @", string(itoaDiv(sbuf[:], uint64(tEnd-runtimeInitTime)/1e6, 3)), "s: ",
|
|
|
|
(tScan-tSweepTerm)/1e6,
|
|
|
|
"+", (tInstallWB-tScan)/1e6,
|
|
|
|
"+", (tMark-tInstallWB)/1e6,
|
|
|
|
"+", (tMarkTerm-tMark)/1e6,
|
|
|
|
"+", (tEnd-tMarkTerm)/1e6, " ms clock, ",
|
|
|
|
int64(stwprocs)*(tScan-tSweepTerm)/1e6,
|
|
|
|
"+", (tInstallWB-tScan)/1e6,
|
|
|
|
"+", int64(stwprocs)*(tMark-tInstallWB)/1e6,
|
|
|
|
"+", (tMarkTerm-tMark)/1e6, "+",
|
|
|
|
int64(stwprocs)*(tEnd-tMarkTerm)/1e6, " ms cpu, ",
|
|
|
|
heap0>>20, "->", heap1>>20, " MB, ",
|
|
|
|
maxprocs, " P")
|
|
|
|
if mode != gcBackgroundMode {
|
|
|
|
print(" (forced)")
|
|
|
|
}
|
|
|
|
print("\n")
|
|
|
|
printunlock()
|
|
|
|
}
|
|
|
|
sweep.nbgsweep = 0
|
|
|
|
sweep.npausesweep = 0
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// now that gc is done, kick off finalizer thread if needed
|
|
|
|
if !concurrentSweep {
|
|
|
|
// give the queued finalizers, if any, a chance to run
|
|
|
|
Gosched()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
// gcMark runs the mark (or, for concurrent GC, mark termination)
|
2015-02-19 13:48:40 -07:00
|
|
|
// STW is in effect at this point.
|
|
|
|
//TODO go:nowritebarrier
|
2015-02-19 14:43:27 -07:00
|
|
|
func gcMark(start_time int64) {
|
2014-11-11 15:05:02 -07:00
|
|
|
if debug.allocfreetrace > 0 {
|
|
|
|
tracegc()
|
|
|
|
}
|
|
|
|
|
2015-03-05 15:33:08 -07:00
|
|
|
if gcphase != _GCmarktermination {
|
|
|
|
throw("in gcMark expecting to see gcphase as _GCmarktermination")
|
|
|
|
}
|
[dev.cc] runtime: delete scalararg, ptrarg; rename onM to systemstack
Scalararg and ptrarg are not "signal safe".
Go code filling them out can be interrupted by a signal,
and then the signal handler runs, and if it also ends up
in Go code that uses scalararg or ptrarg, now the old
values have been smashed.
For the pieces of code that do need to run in a signal handler,
we introduced onM_signalok, which is really just onM
except that the _signalok is meant to convey that the caller
asserts that scalarg and ptrarg will be restored to their old
values after the call (instead of the usual behavior, zeroing them).
Scalararg and ptrarg are also untyped and therefore error-prone.
Go code can always pass a closure instead of using scalararg
and ptrarg; they were only really necessary for C code.
And there's no more C code.
For all these reasons, delete scalararg and ptrarg, converting
the few remaining references to use closures.
Once those are gone, there is no need for a distinction between
onM and onM_signalok, so replace both with a single function
equivalent to the current onM_signalok (that is, it can be called
on any of the curg, g0, and gsignal stacks).
The name onM and the phrase 'm stack' are misnomers,
because on most system an M has two system stacks:
the main thread stack and the signal handling stack.
Correct the misnomer by naming the replacement function systemstack.
Fix a few references to "M stack" in code.
The main motivation for this change is to eliminate scalararg/ptrarg.
Rick and I have already seen them cause problems because
the calling sequence m.ptrarg[0] = p is a heap pointer assignment,
so it gets a write barrier. The write barrier also uses onM, so it has
all the same problems as if it were being invoked by a signal handler.
We worked around this by saving and restoring the old values
and by calling onM_signalok, but there's no point in keeping this nice
home for bugs around any longer.
This CL also changes funcline to return the file name as a result
instead of filling in a passed-in *string. (The *string signature is
left over from when the code was written in and called from C.)
That's arguably an unrelated change, except that once I had done
the ptrarg/scalararg/onM cleanup I started getting false positives
about the *string argument escaping (not allowed in package runtime).
The compiler is wrong, but the easiest fix is to write the code like
Go code instead of like C code. I am a bit worried that the compiler
is wrong because of some use of uninitialized memory in the escape
analysis. If that's the reason, it will go away when we convert the
compiler to Go. (And if not, we'll debug it the next time.)
LGTM=khr
R=r, khr
CC=austin, golang-codereviews, iant, rlh
https://golang.org/cl/174950043
2014-11-12 12:54:31 -07:00
|
|
|
t0 := start_time
|
|
|
|
work.tstart = start_time
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-03-05 15:33:08 -07:00
|
|
|
gcCopySpans() // TODO(rlh): should this be hoisted and done only once? Right now it is done for normal marking and also for checkmarking.
|
2014-11-11 15:05:02 -07:00
|
|
|
|
|
|
|
work.nwait = 0
|
|
|
|
work.ndone = 0
|
|
|
|
work.nproc = uint32(gcprocs())
|
2014-11-15 06:00:38 -07:00
|
|
|
|
2014-12-12 10:41:57 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceGCScanStart()
|
|
|
|
}
|
|
|
|
|
2015-01-28 13:49:26 -07:00
|
|
|
parforsetup(work.markfor, work.nproc, uint32(_RootCount+allglen), false, markroot)
|
2014-11-11 15:05:02 -07:00
|
|
|
if work.nproc > 1 {
|
|
|
|
noteclear(&work.alldone)
|
|
|
|
helpgc(int32(work.nproc))
|
|
|
|
}
|
|
|
|
|
2015-02-05 17:58:18 -07:00
|
|
|
harvestwbufs() // move local workbufs onto global queues where the GC can find them
|
2014-11-11 15:05:02 -07:00
|
|
|
gchelperstart()
|
|
|
|
parfordo(work.markfor)
|
runtime: switch to gcWork abstraction
This converts the garbage collector from directly manipulating work
buffers to using the new gcWork abstraction.
The previous management of work buffers was rather ad hoc. As a
result, switching to the gcWork abstraction changes many details of
work buffer management.
If greyobject fills a work buffer, it can now pull from work.partial
in addition to work.empty.
Previously, gcDrain started with a partial or empty work buffer and
fetched an empty work buffer if it filled its current buffer (in
greyobject). Now, gcDrain starts with a full work buffer and fetches
an partial or empty work buffer if it fills its current buffer (in
greyobject). The original behavior was bad because gcDrain would
immediately drop the empty work buffer returned by greyobject and
fetch a full work buffer, which greyobject was likely to immediately
overflow, fetching another empty work buffer, etc. The new behavior
isn't great at the start because greyobject is likely to immediately
overflow the full buffer, but the steady-state behavior should be more
stable. Both before and after this change, gcDrain fetches a full
work buffer if it drains its current buffer. Basically all of these
choices are bad; the right answer is to use a dual work buffer scheme.
Previously, shade always fetched a work buffer (though usually from
m.currentwbuf), even if the object was already marked. Now it only
fetches a work buffer if it actually greys an object.
Change-Id: I8b880ed660eb63135236fa5d5678f0c1c041881f
Reviewed-on: https://go-review.googlesource.com/5232
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Rick Hudson <rlh@golang.org>
2015-02-17 08:53:31 -07:00
|
|
|
var gcw gcWork
|
|
|
|
gcDrain(&gcw)
|
|
|
|
gcw.dispose()
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2014-11-15 06:00:38 -07:00
|
|
|
if work.full != 0 {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("work.full != 0")
|
2014-11-15 06:00:38 -07:00
|
|
|
}
|
|
|
|
if work.partial != 0 {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("work.partial != 0")
|
2014-11-15 06:00:38 -07:00
|
|
|
}
|
|
|
|
|
2014-11-11 15:05:02 -07:00
|
|
|
if work.nproc > 1 {
|
|
|
|
notesleep(&work.alldone)
|
|
|
|
}
|
|
|
|
|
2014-12-12 10:41:57 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceGCScanDone()
|
|
|
|
}
|
|
|
|
|
2014-11-11 15:05:02 -07:00
|
|
|
shrinkfinish()
|
|
|
|
|
|
|
|
cachestats()
|
|
|
|
// next_gc calculation is tricky with concurrent sweep since we don't know size of live heap
|
|
|
|
// conservatively set next_gc to high value assuming that everything is live
|
|
|
|
// concurrent/lazy sweep will reduce this number while discovering new garbage
|
|
|
|
memstats.next_gc = memstats.heap_alloc + memstats.heap_alloc*uint64(gcpercent)/100
|
2015-01-28 13:57:46 -07:00
|
|
|
if memstats.next_gc < heapminimum {
|
|
|
|
memstats.next_gc = heapminimum
|
|
|
|
}
|
|
|
|
|
2014-12-12 10:41:57 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceNextGC()
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
|
|
|
t4 := nanotime()
|
|
|
|
atomicstore64(&memstats.last_gc, uint64(unixnanotime())) // must be Unix time to make sense to user
|
|
|
|
memstats.pause_ns[memstats.numgc%uint32(len(memstats.pause_ns))] = uint64(t4 - t0)
|
|
|
|
memstats.pause_end[memstats.numgc%uint32(len(memstats.pause_end))] = uint64(t4)
|
|
|
|
memstats.pause_total_ns += uint64(t4 - t0)
|
2015-02-19 14:43:27 -07:00
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
func gcSweep(mode int) {
|
2015-03-05 15:33:08 -07:00
|
|
|
if gcphase != _GCoff {
|
|
|
|
throw("gcSweep being done but phase is not GCoff")
|
|
|
|
}
|
2015-02-19 14:43:27 -07:00
|
|
|
gcCopySpans()
|
2014-11-15 06:00:38 -07:00
|
|
|
|
2015-02-19 14:21:42 -07:00
|
|
|
lock(&mheap_.lock)
|
2014-11-11 15:05:02 -07:00
|
|
|
mheap_.sweepgen += 2
|
|
|
|
mheap_.sweepdone = 0
|
|
|
|
sweep.spanidx = 0
|
|
|
|
unlock(&mheap_.lock)
|
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
if !_ConcurrentSweep || mode == gcForceBlockMode {
|
|
|
|
// Special case synchronous sweep.
|
2014-11-11 15:05:02 -07:00
|
|
|
// Sweep all spans eagerly.
|
|
|
|
for sweepone() != ^uintptr(0) {
|
|
|
|
sweep.npausesweep++
|
|
|
|
}
|
|
|
|
// Do an additional mProf_GC, because all 'free' events are now real as well.
|
|
|
|
mProf_GC()
|
2015-02-19 14:43:27 -07:00
|
|
|
mProf_GC()
|
|
|
|
return
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
// Background sweep.
|
|
|
|
lock(&sweep.lock)
|
2015-03-05 14:04:17 -07:00
|
|
|
if sweep.parked {
|
2015-02-19 14:43:27 -07:00
|
|
|
sweep.parked = false
|
2015-02-21 11:01:40 -07:00
|
|
|
ready(sweep.g, 0)
|
2015-02-19 14:43:27 -07:00
|
|
|
}
|
|
|
|
unlock(&sweep.lock)
|
2014-11-11 15:05:02 -07:00
|
|
|
mProf_GC()
|
2015-02-19 14:43:27 -07:00
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 14:43:27 -07:00
|
|
|
func gcCopySpans() {
|
|
|
|
// Cache runtime.mheap_.allspans in work.spans to avoid conflicts with
|
|
|
|
// resizing/freeing allspans.
|
|
|
|
// New spans can be created while GC progresses, but they are not garbage for
|
|
|
|
// this round:
|
|
|
|
// - new stack spans can be created even while the world is stopped.
|
|
|
|
// - new malloc spans can be created during the concurrent sweep
|
|
|
|
// Even if this is stop-the-world, a concurrent exitsyscall can allocate a stack from heap.
|
|
|
|
lock(&mheap_.lock)
|
|
|
|
// Free the old cached mark array if necessary.
|
|
|
|
if work.spans != nil && &work.spans[0] != &h_allspans[0] {
|
|
|
|
sysFree(unsafe.Pointer(&work.spans[0]), uintptr(len(work.spans))*unsafe.Sizeof(work.spans[0]), &memstats.other_sys)
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-02-19 14:43:27 -07:00
|
|
|
// Cache the current array for sweeping.
|
|
|
|
mheap_.gcspans = mheap_.allspans
|
|
|
|
work.spans = h_allspans
|
|
|
|
unlock(&mheap_.lock)
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-24 20:20:38 -07:00
|
|
|
// gcResetGState resets the GC state of all G's and returns the length
|
|
|
|
// of allgs.
|
2015-02-24 20:29:33 -07:00
|
|
|
func gcResetGState() (numgs int) {
|
2015-02-24 20:20:38 -07:00
|
|
|
// This may be called during a concurrent phase, so make sure
|
|
|
|
// allgs doesn't change.
|
|
|
|
lock(&allglock)
|
2015-02-24 20:29:33 -07:00
|
|
|
for _, gp := range allgs {
|
2015-02-24 20:20:38 -07:00
|
|
|
gp.gcworkdone = false // set to true in gcphasework
|
|
|
|
gp.gcscanvalid = false // stack has not been scanned
|
|
|
|
}
|
2015-02-24 20:29:33 -07:00
|
|
|
numgs = len(allgs)
|
2015-02-24 20:20:38 -07:00
|
|
|
unlock(&allglock)
|
2015-02-24 20:29:33 -07:00
|
|
|
return
|
2015-02-24 20:20:38 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// Hooks for other packages
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
var poolcleanup func()
|
|
|
|
|
|
|
|
//go:linkname sync_runtime_registerPoolCleanup sync.runtime_registerPoolCleanup
|
|
|
|
func sync_runtime_registerPoolCleanup(f func()) {
|
|
|
|
poolcleanup = f
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
func clearpools() {
|
|
|
|
// clear sync.Pools
|
|
|
|
if poolcleanup != nil {
|
|
|
|
poolcleanup()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-02 14:33:02 -07:00
|
|
|
// Clear central sudog cache.
|
|
|
|
// Leave per-P caches alone, they have strictly bounded size.
|
|
|
|
// Disconnect cached list before dropping it on the floor,
|
|
|
|
// so that a dangling ref to one entry does not pin all of them.
|
|
|
|
lock(&sched.sudoglock)
|
|
|
|
var sg, sgnext *sudog
|
|
|
|
for sg = sched.sudogcache; sg != nil; sg = sgnext {
|
|
|
|
sgnext = sg.next
|
|
|
|
sg.next = nil
|
|
|
|
}
|
|
|
|
sched.sudogcache = nil
|
|
|
|
unlock(&sched.sudoglock)
|
|
|
|
|
2015-02-05 06:35:41 -07:00
|
|
|
// Clear central defer pools.
|
|
|
|
// Leave per-P pools alone, they have strictly bounded size.
|
|
|
|
lock(&sched.deferlock)
|
|
|
|
for i := range sched.deferpool {
|
|
|
|
// disconnect cached list before dropping it on the floor,
|
|
|
|
// so that a dangling ref to one entry does not pin all of them.
|
|
|
|
var d, dlink *_defer
|
|
|
|
for d = sched.deferpool[i]; d != nil; d = dlink {
|
|
|
|
dlink = d.link
|
|
|
|
d.link = nil
|
|
|
|
}
|
|
|
|
sched.deferpool[i] = nil
|
|
|
|
}
|
|
|
|
unlock(&sched.deferlock)
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
for _, p := range &allp {
|
|
|
|
if p == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
// clear tinyalloc pool
|
|
|
|
if c := p.mcache; c != nil {
|
|
|
|
c.tiny = nil
|
|
|
|
c.tinyoffset = 0
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-02-19 11:38:46 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Timing
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
//go:nowritebarrier
|
|
|
|
func gchelper() {
|
|
|
|
_g_ := getg()
|
|
|
|
_g_.m.traceback = 2
|
|
|
|
gchelperstart()
|
|
|
|
|
|
|
|
if trace.enabled {
|
|
|
|
traceGCScanStart()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// parallel mark for over GC roots
|
|
|
|
parfordo(work.markfor)
|
|
|
|
if gcphase != _GCscan {
|
|
|
|
var gcw gcWork
|
|
|
|
gcDrain(&gcw) // blocks in getfull
|
|
|
|
gcw.dispose()
|
|
|
|
}
|
2014-11-11 15:05:02 -07:00
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
if trace.enabled {
|
|
|
|
traceGCScanDone()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-02-19 11:38:46 -07:00
|
|
|
|
|
|
|
nproc := work.nproc // work.nproc can change right after we increment work.ndone
|
|
|
|
if xadd(&work.ndone, +1) == nproc-1 {
|
|
|
|
notewakeup(&work.alldone)
|
|
|
|
}
|
|
|
|
_g_.m.traceback = 0
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
func gchelperstart() {
|
|
|
|
_g_ := getg()
|
|
|
|
|
|
|
|
if _g_.m.helpgc < 0 || _g_.m.helpgc >= _MaxGcproc {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("gchelperstart: bad m->helpgc")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
if _g_ != _g_.m.g0 {
|
2014-12-27 21:58:00 -07:00
|
|
|
throw("gchelper not running on g0 stack")
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 11:38:46 -07:00
|
|
|
// gcchronograph holds timer information related to GC phases
|
|
|
|
// max records the maximum time spent in each GC phase since GCstarttimes.
|
|
|
|
// total records the total time spent in each GC phase since GCstarttimes.
|
|
|
|
// cycle records the absolute time (as returned by nanoseconds()) that each GC phase last started at.
|
|
|
|
type gcchronograph struct {
|
|
|
|
count int64
|
|
|
|
verbose int64
|
|
|
|
maxpause int64
|
|
|
|
max gctimes
|
|
|
|
total gctimes
|
|
|
|
cycle gctimes
|
|
|
|
}
|
|
|
|
|
|
|
|
// gctimes records the time in nanoseconds of each phase of the concurrent GC.
|
|
|
|
type gctimes struct {
|
|
|
|
sweepterm int64 // stw
|
|
|
|
scan int64
|
|
|
|
installmarkwb int64 // stw
|
|
|
|
mark int64
|
|
|
|
markterm int64 // stw
|
|
|
|
sweep int64
|
|
|
|
}
|
|
|
|
|
|
|
|
var gctimer gcchronograph
|
|
|
|
|
|
|
|
// GCstarttimes initializes the gc times. All previous times are lost.
|
|
|
|
func GCstarttimes(verbose int64) {
|
|
|
|
gctimer = gcchronograph{verbose: verbose}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GCendtimes stops the gc timers.
|
|
|
|
func GCendtimes() {
|
|
|
|
gctimer.verbose = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// calctimes converts gctimer.cycle into the elapsed times, updates gctimer.total
|
|
|
|
// and updates gctimer.max with the max pause time.
|
|
|
|
func calctimes() gctimes {
|
|
|
|
var times gctimes
|
|
|
|
|
|
|
|
var max = func(a, b int64) int64 {
|
|
|
|
if a > b {
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
|
|
|
|
times.sweepterm = gctimer.cycle.scan - gctimer.cycle.sweepterm
|
|
|
|
gctimer.total.sweepterm += times.sweepterm
|
|
|
|
gctimer.max.sweepterm = max(gctimer.max.sweepterm, times.sweepterm)
|
|
|
|
gctimer.maxpause = max(gctimer.maxpause, gctimer.max.sweepterm)
|
|
|
|
|
|
|
|
times.scan = gctimer.cycle.installmarkwb - gctimer.cycle.scan
|
|
|
|
gctimer.total.scan += times.scan
|
|
|
|
gctimer.max.scan = max(gctimer.max.scan, times.scan)
|
|
|
|
|
|
|
|
times.installmarkwb = gctimer.cycle.mark - gctimer.cycle.installmarkwb
|
|
|
|
gctimer.total.installmarkwb += times.installmarkwb
|
|
|
|
gctimer.max.installmarkwb = max(gctimer.max.installmarkwb, times.installmarkwb)
|
|
|
|
gctimer.maxpause = max(gctimer.maxpause, gctimer.max.installmarkwb)
|
|
|
|
|
|
|
|
times.mark = gctimer.cycle.markterm - gctimer.cycle.mark
|
|
|
|
gctimer.total.mark += times.mark
|
|
|
|
gctimer.max.mark = max(gctimer.max.mark, times.mark)
|
|
|
|
|
|
|
|
times.markterm = gctimer.cycle.sweep - gctimer.cycle.markterm
|
|
|
|
gctimer.total.markterm += times.markterm
|
|
|
|
gctimer.max.markterm = max(gctimer.max.markterm, times.markterm)
|
|
|
|
gctimer.maxpause = max(gctimer.maxpause, gctimer.max.markterm)
|
|
|
|
|
|
|
|
return times
|
|
|
|
}
|
|
|
|
|
|
|
|
// GCprinttimes prints latency information in nanoseconds about various
|
|
|
|
// phases in the GC. The information for each phase includes the maximum pause
|
|
|
|
// and total time since the most recent call to GCstarttimes as well as
|
|
|
|
// the information from the most recent Concurent GC cycle. Calls from the
|
|
|
|
// application to runtime.GC() are ignored.
|
|
|
|
func GCprinttimes() {
|
|
|
|
if gctimer.verbose == 0 {
|
|
|
|
println("GC timers not enabled")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Explicitly put times on the heap so printPhase can use it.
|
|
|
|
times := new(gctimes)
|
|
|
|
*times = calctimes()
|
|
|
|
cycletime := gctimer.cycle.sweep - gctimer.cycle.sweepterm
|
|
|
|
pause := times.sweepterm + times.installmarkwb + times.markterm
|
|
|
|
gomaxprocs := GOMAXPROCS(-1)
|
|
|
|
|
|
|
|
printlock()
|
|
|
|
print("GC: #", gctimer.count, " ", cycletime, "ns @", gctimer.cycle.sweepterm, " pause=", pause, " maxpause=", gctimer.maxpause, " goroutines=", allglen, " gomaxprocs=", gomaxprocs, "\n")
|
|
|
|
printPhase := func(label string, get func(*gctimes) int64, procs int) {
|
|
|
|
print("GC: ", label, " ", get(times), "ns\tmax=", get(&gctimer.max), "\ttotal=", get(&gctimer.total), "\tprocs=", procs, "\n")
|
|
|
|
}
|
|
|
|
printPhase("sweep term:", func(t *gctimes) int64 { return t.sweepterm }, gomaxprocs)
|
|
|
|
printPhase("scan: ", func(t *gctimes) int64 { return t.scan }, 1)
|
|
|
|
printPhase("install wb:", func(t *gctimes) int64 { return t.installmarkwb }, gomaxprocs)
|
|
|
|
printPhase("mark: ", func(t *gctimes) int64 { return t.mark }, 1)
|
|
|
|
printPhase("mark term: ", func(t *gctimes) int64 { return t.markterm }, gomaxprocs)
|
|
|
|
printunlock()
|
2014-11-11 15:05:02 -07:00
|
|
|
}
|
2015-03-26 16:48:42 -06:00
|
|
|
|
|
|
|
// itoaDiv formats val/(10**dec) into buf.
|
|
|
|
func itoaDiv(buf []byte, val uint64, dec int) []byte {
|
|
|
|
i := len(buf) - 1
|
|
|
|
idec := i - dec
|
|
|
|
for val >= 10 || i >= idec {
|
|
|
|
buf[i] = byte(val%10 + '0')
|
|
|
|
i--
|
|
|
|
if i == idec {
|
|
|
|
buf[i] = '.'
|
|
|
|
i--
|
|
|
|
}
|
|
|
|
val /= 10
|
|
|
|
}
|
|
|
|
buf[i] = byte(val + '0')
|
|
|
|
return buf[i:]
|
|
|
|
}
|