...

Source file src/runtime/mgcsweep.go

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

View as plain text