github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/runtime/mgcsweep.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Garbage collector: sweeping
     6  
     7  package runtime
     8  
     9  import (
    10  	"runtime/internal/atomic"
    11  	"unsafe"
    12  )
    13  
    14  var sweep sweepdata
    15  
    16  // State of background sweep.
    17  type sweepdata struct {
    18  	lock    mutex
    19  	g       *g
    20  	parked  bool
    21  	started bool
    22  
    23  	nbgsweep    uint32
    24  	npausesweep uint32
    25  }
    26  
    27  // finishsweep_m ensures that all spans are swept.
    28  //
    29  // The world must be stopped. This ensures there are no sweeps in
    30  // progress.
    31  //
    32  //go:nowritebarrier
    33  func finishsweep_m() {
    34  	// Sweeping must be complete before marking commences, so
    35  	// sweep any unswept spans. If this is a concurrent GC, there
    36  	// shouldn't be any spans left to sweep, so this should finish
    37  	// instantly. If GC was forced before the concurrent sweep
    38  	// finished, there may be spans to sweep.
    39  	for sweepone() != ^uintptr(0) {
    40  		sweep.npausesweep++
    41  	}
    42  
    43  	nextMarkBitArenaEpoch()
    44  }
    45  
    46  func bgsweep(c chan int) {
    47  	sweep.g = getg()
    48  
    49  	lock(&sweep.lock)
    50  	sweep.parked = true
    51  	c <- 1
    52  	goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
    53  
    54  	for {
    55  		for gosweepone() != ^uintptr(0) {
    56  			sweep.nbgsweep++
    57  			Gosched()
    58  		}
    59  		for freeSomeWbufs(true) {
    60  			Gosched()
    61  		}
    62  		lock(&sweep.lock)
    63  		if !gosweepdone() {
    64  			// This can happen if a GC runs between
    65  			// gosweepone returning ^0 above
    66  			// and the lock being acquired.
    67  			unlock(&sweep.lock)
    68  			continue
    69  		}
    70  		sweep.parked = true
    71  		goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
    72  	}
    73  }
    74  
    75  // sweeps one span
    76  // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep
    77  //go:nowritebarrier
    78  func sweepone() uintptr {
    79  	_g_ := getg()
    80  	sweepRatio := mheap_.sweepPagesPerByte // For debugging
    81  
    82  	// increment locks to ensure that the goroutine is not preempted
    83  	// in the middle of sweep thus leaving the span in an inconsistent state for next GC
    84  	_g_.m.locks++
    85  	if atomic.Load(&mheap_.sweepdone) != 0 {
    86  		_g_.m.locks--
    87  		return ^uintptr(0)
    88  	}
    89  	atomic.Xadd(&mheap_.sweepers, +1)
    90  
    91  	npages := ^uintptr(0)
    92  	sg := mheap_.sweepgen
    93  	for {
    94  		s := mheap_.sweepSpans[1-sg/2%2].pop()
    95  		if s == nil {
    96  			atomic.Store(&mheap_.sweepdone, 1)
    97  			break
    98  		}
    99  		if s.state != mSpanInUse {
   100  			// This can happen if direct sweeping already
   101  			// swept this span, but in that case the sweep
   102  			// generation should always be up-to-date.
   103  			if s.sweepgen != sg {
   104  				print("runtime: bad span s.state=", s.state, " s.sweepgen=", s.sweepgen, " sweepgen=", sg, "\n")
   105  				throw("non in-use span in unswept list")
   106  			}
   107  			continue
   108  		}
   109  		if s.sweepgen != sg-2 || !atomic.Cas(&s.sweepgen, sg-2, sg-1) {
   110  			continue
   111  		}
   112  		npages = s.npages
   113  		if !s.sweep(false) {
   114  			// Span is still in-use, so this returned no
   115  			// pages to the heap and the span needs to
   116  			// move to the swept in-use list.
   117  			npages = 0
   118  		}
   119  		break
   120  	}
   121  
   122  	// Decrement the number of active sweepers and if this is the
   123  	// last one print trace information.
   124  	if atomic.Xadd(&mheap_.sweepers, -1) == 0 && atomic.Load(&mheap_.sweepdone) != 0 {
   125  		if debug.gcpacertrace > 0 {
   126  			print("pacer: sweep done at heap size ", memstats.heap_live>>20, "MB; allocated ", (memstats.heap_live-mheap_.sweepHeapLiveBasis)>>20, "MB during sweep; swept ", mheap_.pagesSwept, " pages at ", sweepRatio, " pages/byte\n")
   127  		}
   128  	}
   129  	_g_.m.locks--
   130  	return npages
   131  }
   132  
   133  //go:nowritebarrier
   134  func gosweepone() uintptr {
   135  	var ret uintptr
   136  	systemstack(func() {
   137  		ret = sweepone()
   138  	})
   139  	return ret
   140  }
   141  
   142  //go:nowritebarrier
   143  func gosweepdone() bool {
   144  	return mheap_.sweepdone != 0
   145  }
   146  
   147  // Returns only when span s has been swept.
   148  //go:nowritebarrier
   149  func (s *mspan) ensureSwept() {
   150  	// Caller must disable preemption.
   151  	// Otherwise when this function returns the span can become unswept again
   152  	// (if GC is triggered on another goroutine).
   153  	_g_ := getg()
   154  	if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 {
   155  		throw("MSpan_EnsureSwept: m is not locked")
   156  	}
   157  
   158  	sg := mheap_.sweepgen
   159  	if atomic.Load(&s.sweepgen) == sg {
   160  		return
   161  	}
   162  	// The caller must be sure that the span is a MSpanInUse span.
   163  	if atomic.Cas(&s.sweepgen, sg-2, sg-1) {
   164  		s.sweep(false)
   165  		return
   166  	}
   167  	// unfortunate condition, and we don't have efficient means to wait
   168  	for atomic.Load(&s.sweepgen) != sg {
   169  		osyield()
   170  	}
   171  }
   172  
   173  // Sweep frees or collects finalizers for blocks not marked in the mark phase.
   174  // It clears the mark bits in preparation for the next GC round.
   175  // Returns true if the span was returned to heap.
   176  // If preserve=true, don't return it to heap nor relink in MCentral lists;
   177  // caller takes care of it.
   178  //TODO go:nowritebarrier
   179  func (s *mspan) sweep(preserve bool) bool {
   180  	// It's critical that we enter this function with preemption disabled,
   181  	// GC must not start while we are in the middle of this function.
   182  	_g_ := getg()
   183  	if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 {
   184  		throw("MSpan_Sweep: m is not locked")
   185  	}
   186  	sweepgen := mheap_.sweepgen
   187  	if s.state != mSpanInUse || s.sweepgen != sweepgen-1 {
   188  		print("MSpan_Sweep: state=", s.state, " sweepgen=", s.sweepgen, " mheap.sweepgen=", sweepgen, "\n")
   189  		throw("MSpan_Sweep: bad span state")
   190  	}
   191  
   192  	if trace.enabled {
   193  		traceGCSweepSpan(s.npages * _PageSize)
   194  	}
   195  
   196  	atomic.Xadd64(&mheap_.pagesSwept, int64(s.npages))
   197  
   198  	spc := s.spanclass
   199  	size := s.elemsize
   200  	res := false
   201  
   202  	c := _g_.m.mcache
   203  	freeToHeap := false
   204  
   205  	// The allocBits indicate which unmarked objects don't need to be
   206  	// processed since they were free at the end of the last GC cycle
   207  	// and were not allocated since then.
   208  	// If the allocBits index is >= s.freeindex and the bit
   209  	// is not marked then the object remains unallocated
   210  	// since the last GC.
   211  	// This situation is analogous to being on a freelist.
   212  
   213  	// Unlink & free special records for any objects we're about to free.
   214  	// Two complications here:
   215  	// 1. An object can have both finalizer and profile special records.
   216  	//    In such case we need to queue finalizer for execution,
   217  	//    mark the object as live and preserve the profile special.
   218  	// 2. A tiny object can have several finalizers setup for different offsets.
   219  	//    If such object is not marked, we need to queue all finalizers at once.
   220  	// Both 1 and 2 are possible at the same time.
   221  	specialp := &s.specials
   222  	special := *specialp
   223  	for special != nil {
   224  		// A finalizer can be set for an inner byte of an object, find object beginning.
   225  		objIndex := uintptr(special.offset) / size
   226  		p := s.base() + objIndex*size
   227  		mbits := s.markBitsForIndex(objIndex)
   228  		if !mbits.isMarked() {
   229  			// This object is not marked and has at least one special record.
   230  			// Pass 1: see if it has at least one finalizer.
   231  			hasFin := false
   232  			endOffset := p - s.base() + size
   233  			for tmp := special; tmp != nil && uintptr(tmp.offset) < endOffset; tmp = tmp.next {
   234  				if tmp.kind == _KindSpecialFinalizer {
   235  					// Stop freeing of object if it has a finalizer.
   236  					mbits.setMarkedNonAtomic()
   237  					hasFin = true
   238  					break
   239  				}
   240  			}
   241  			// Pass 2: queue all finalizers _or_ handle profile record.
   242  			for special != nil && uintptr(special.offset) < endOffset {
   243  				// Find the exact byte for which the special was setup
   244  				// (as opposed to object beginning).
   245  				p := s.base() + uintptr(special.offset)
   246  				if special.kind == _KindSpecialFinalizer || !hasFin {
   247  					// Splice out special record.
   248  					y := special
   249  					special = special.next
   250  					*specialp = special
   251  					freespecial(y, unsafe.Pointer(p), size)
   252  				} else {
   253  					// This is profile record, but the object has finalizers (so kept alive).
   254  					// Keep special record.
   255  					specialp = &special.next
   256  					special = *specialp
   257  				}
   258  			}
   259  		} else {
   260  			// object is still live: keep special record
   261  			specialp = &special.next
   262  			special = *specialp
   263  		}
   264  	}
   265  
   266  	if debug.allocfreetrace != 0 || raceenabled || msanenabled {
   267  		// Find all newly freed objects. This doesn't have to
   268  		// efficient; allocfreetrace has massive overhead.
   269  		mbits := s.markBitsForBase()
   270  		abits := s.allocBitsForIndex(0)
   271  		for i := uintptr(0); i < s.nelems; i++ {
   272  			if !mbits.isMarked() && (abits.index < s.freeindex || abits.isMarked()) {
   273  				x := s.base() + i*s.elemsize
   274  				if debug.allocfreetrace != 0 {
   275  					tracefree(unsafe.Pointer(x), size)
   276  				}
   277  				if raceenabled {
   278  					racefree(unsafe.Pointer(x), size)
   279  				}
   280  				if msanenabled {
   281  					msanfree(unsafe.Pointer(x), size)
   282  				}
   283  			}
   284  			mbits.advance()
   285  			abits.advance()
   286  		}
   287  	}
   288  
   289  	// Count the number of free objects in this span.
   290  	nalloc := uint16(s.countAlloc())
   291  	if spc.sizeclass() == 0 && nalloc == 0 {
   292  		s.needzero = 1
   293  		freeToHeap = true
   294  	}
   295  	nfreed := s.allocCount - nalloc
   296  	if nalloc > s.allocCount {
   297  		print("runtime: nelems=", s.nelems, " nalloc=", nalloc, " previous allocCount=", s.allocCount, " nfreed=", nfreed, "\n")
   298  		throw("sweep increased allocation count")
   299  	}
   300  
   301  	s.allocCount = nalloc
   302  	wasempty := s.nextFreeIndex() == s.nelems
   303  	s.freeindex = 0 // reset allocation index to start of span.
   304  	if trace.enabled {
   305  		getg().m.p.ptr().traceReclaimed += uintptr(nfreed) * s.elemsize
   306  	}
   307  
   308  	// gcmarkBits becomes the allocBits.
   309  	// get a fresh cleared gcmarkBits in preparation for next GC
   310  	s.allocBits = s.gcmarkBits
   311  	s.gcmarkBits = newMarkBits(s.nelems)
   312  
   313  	// Initialize alloc bits cache.
   314  	s.refillAllocCache(0)
   315  
   316  	// We need to set s.sweepgen = h.sweepgen only when all blocks are swept,
   317  	// because of the potential for a concurrent free/SetFinalizer.
   318  	// But we need to set it before we make the span available for allocation
   319  	// (return it to heap or mcentral), because allocation code assumes that a
   320  	// span is already swept if available for allocation.
   321  	if freeToHeap || nfreed == 0 {
   322  		// The span must be in our exclusive ownership until we update sweepgen,
   323  		// check for potential races.
   324  		if s.state != mSpanInUse || s.sweepgen != sweepgen-1 {
   325  			print("MSpan_Sweep: state=", s.state, " sweepgen=", s.sweepgen, " mheap.sweepgen=", sweepgen, "\n")
   326  			throw("MSpan_Sweep: bad span state after sweep")
   327  		}
   328  		// Serialization point.
   329  		// At this point the mark bits are cleared and allocation ready
   330  		// to go so release the span.
   331  		atomic.Store(&s.sweepgen, sweepgen)
   332  	}
   333  
   334  	if nfreed > 0 && spc.sizeclass() != 0 {
   335  		c.local_nsmallfree[spc.sizeclass()] += uintptr(nfreed)
   336  		res = mheap_.central[spc].mcentral.freeSpan(s, preserve, wasempty)
   337  		// MCentral_FreeSpan updates sweepgen
   338  	} else if freeToHeap {
   339  		// Free large span to heap
   340  
   341  		// NOTE(rsc,dvyukov): The original implementation of efence
   342  		// in CL 22060046 used SysFree instead of SysFault, so that
   343  		// the operating system would eventually give the memory
   344  		// back to us again, so that an efence program could run
   345  		// longer without running out of memory. Unfortunately,
   346  		// calling SysFree here without any kind of adjustment of the
   347  		// heap data structures means that when the memory does
   348  		// come back to us, we have the wrong metadata for it, either in
   349  		// the MSpan structures or in the garbage collection bitmap.
   350  		// Using SysFault here means that the program will run out of
   351  		// memory fairly quickly in efence mode, but at least it won't
   352  		// have mysterious crashes due to confused memory reuse.
   353  		// It should be possible to switch back to SysFree if we also
   354  		// implement and then call some kind of MHeap_DeleteSpan.
   355  		if debug.efence > 0 {
   356  			s.limit = 0 // prevent mlookup from finding this span
   357  			sysFault(unsafe.Pointer(s.base()), size)
   358  		} else {
   359  			mheap_.freeSpan(s, 1)
   360  		}
   361  		c.local_nlargefree++
   362  		c.local_largefree += size
   363  		res = true
   364  	}
   365  	if !res {
   366  		// The span has been swept and is still in-use, so put
   367  		// it on the swept in-use list.
   368  		mheap_.sweepSpans[sweepgen/2%2].push(s)
   369  	}
   370  	return res
   371  }
   372  
   373  // deductSweepCredit deducts sweep credit for allocating a span of
   374  // size spanBytes. This must be performed *before* the span is
   375  // allocated to ensure the system has enough credit. If necessary, it
   376  // performs sweeping to prevent going in to debt. If the caller will
   377  // also sweep pages (e.g., for a large allocation), it can pass a
   378  // non-zero callerSweepPages to leave that many pages unswept.
   379  //
   380  // deductSweepCredit makes a worst-case assumption that all spanBytes
   381  // bytes of the ultimately allocated span will be available for object
   382  // allocation.
   383  //
   384  // deductSweepCredit is the core of the "proportional sweep" system.
   385  // It uses statistics gathered by the garbage collector to perform
   386  // enough sweeping so that all pages are swept during the concurrent
   387  // sweep phase between GC cycles.
   388  //
   389  // mheap_ must NOT be locked.
   390  func deductSweepCredit(spanBytes uintptr, callerSweepPages uintptr) {
   391  	if mheap_.sweepPagesPerByte == 0 {
   392  		// Proportional sweep is done or disabled.
   393  		return
   394  	}
   395  
   396  	if trace.enabled {
   397  		traceGCSweepStart()
   398  	}
   399  
   400  retry:
   401  	sweptBasis := atomic.Load64(&mheap_.pagesSweptBasis)
   402  
   403  	// Fix debt if necessary.
   404  	newHeapLive := uintptr(atomic.Load64(&memstats.heap_live)-mheap_.sweepHeapLiveBasis) + spanBytes
   405  	pagesTarget := int64(mheap_.sweepPagesPerByte*float64(newHeapLive)) - int64(callerSweepPages)
   406  	for pagesTarget > int64(atomic.Load64(&mheap_.pagesSwept)-sweptBasis) {
   407  		if gosweepone() == ^uintptr(0) {
   408  			mheap_.sweepPagesPerByte = 0
   409  			break
   410  		}
   411  		if atomic.Load64(&mheap_.pagesSweptBasis) != sweptBasis {
   412  			// Sweep pacing changed. Recompute debt.
   413  			goto retry
   414  		}
   415  	}
   416  
   417  	if trace.enabled {
   418  		traceGCSweepDone()
   419  	}
   420  }