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