github.com/c0deoo1/golang1.5@v0.0.0-20220525150107-c87c805d4593/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 "unsafe"
    10  
    11  var sweep sweepdata
    12  
    13  // State of background sweep.
    14  type sweepdata struct {
    15  	lock    mutex
    16  	g       *g
    17  	parked  bool
    18  	started bool
    19  
    20  	spanidx uint32 // background sweeper position
    21  
    22  	nbgsweep    uint32
    23  	npausesweep uint32
    24  }
    25  
    26  //go:nowritebarrier
    27  func finishsweep_m() {
    28  	// The world is stopped so we should be able to complete the sweeps
    29  	// quickly.
    30  	for sweepone() != ^uintptr(0) {
    31  		sweep.npausesweep++
    32  	}
    33  
    34  	// There may be some other spans being swept concurrently that
    35  	// we need to wait for. If finishsweep_m is done with the world stopped
    36  	// this code is not required.
    37  	sg := mheap_.sweepgen
    38  	for _, s := range work.spans {
    39  		if s.sweepgen != sg && s.state == _MSpanInUse {
    40  			mSpan_EnsureSwept(s)
    41  		}
    42  	}
    43  }
    44  
    45  func bgsweep(c chan int) {
    46  	sweep.g = getg()
    47  
    48  	lock(&sweep.lock)
    49  	sweep.parked = true
    50  	c <- 1
    51  	goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
    52  
    53  	for {
    54  		for gosweepone() != ^uintptr(0) {
    55  			sweep.nbgsweep++
    56  			Gosched()
    57  		}
    58  		lock(&sweep.lock)
    59  		if !gosweepdone() {
    60  			// This can happen if a GC runs between
    61  			// gosweepone returning ^0 above
    62  			// and the lock being acquired.
    63  			unlock(&sweep.lock)
    64  			continue
    65  		}
    66  		sweep.parked = true
    67  		goparkunlock(&sweep.lock, "GC sweep wait", traceEvGoBlock, 1)
    68  	}
    69  }
    70  
    71  // sweeps one span
    72  // returns number of pages returned to heap, or ^uintptr(0) if there is nothing to sweep
    73  //go:nowritebarrier
    74  func sweepone() uintptr {
    75  	_g_ := getg()
    76  
    77  	// increment locks to ensure that the goroutine is not preempted
    78  	// in the middle of sweep thus leaving the span in an inconsistent state for next GC
    79  	_g_.m.locks++
    80  	sg := mheap_.sweepgen
    81  	for {
    82  		idx := xadd(&sweep.spanidx, 1) - 1
    83  		if idx >= uint32(len(work.spans)) {
    84  			mheap_.sweepdone = 1
    85  			_g_.m.locks--
    86  			return ^uintptr(0)
    87  		}
    88  		s := work.spans[idx]
    89  		if s.state != mSpanInUse {
    90  			s.sweepgen = sg
    91  			continue
    92  		}
    93  		if s.sweepgen != sg-2 || !cas(&s.sweepgen, sg-2, sg-1) {
    94  			continue
    95  		}
    96  		npages := s.npages
    97  		if !mSpan_Sweep(s, false) {
    98  			npages = 0
    99  		}
   100  		_g_.m.locks--
   101  		return npages
   102  	}
   103  }
   104  
   105  //go:nowritebarrier
   106  func gosweepone() uintptr {
   107  	var ret uintptr
   108  	systemstack(func() {
   109  		ret = sweepone()
   110  	})
   111  	return ret
   112  }
   113  
   114  //go:nowritebarrier
   115  func gosweepdone() bool {
   116  	return mheap_.sweepdone != 0
   117  }
   118  
   119  // Returns only when span s has been swept.
   120  //go:nowritebarrier
   121  func mSpan_EnsureSwept(s *mspan) {
   122  	// Caller must disable preemption.
   123  	// Otherwise when this function returns the span can become unswept again
   124  	// (if GC is triggered on another goroutine).
   125  	_g_ := getg()
   126  	if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 {
   127  		throw("MSpan_EnsureSwept: m is not locked")
   128  	}
   129  
   130  	sg := mheap_.sweepgen
   131  	if atomicload(&s.sweepgen) == sg {
   132  		return
   133  	}
   134  	// The caller must be sure that the span is a MSpanInUse span.
   135  	if cas(&s.sweepgen, sg-2, sg-1) {
   136  		mSpan_Sweep(s, false)
   137  		return
   138  	}
   139  	// unfortunate condition, and we don't have efficient means to wait
   140  	for atomicload(&s.sweepgen) != sg {
   141  		osyield()
   142  	}
   143  }
   144  
   145  // Sweep frees or collects finalizers for blocks not marked in the mark phase.
   146  // It clears the mark bits in preparation for the next GC round.
   147  // Returns true if the span was returned to heap.
   148  // If preserve=true, don't return it to heap nor relink in MCentral lists;
   149  // caller takes care of it.
   150  //TODO go:nowritebarrier
   151  func mSpan_Sweep(s *mspan, preserve bool) bool {
   152  	// It's critical that we enter this function with preemption disabled,
   153  	// GC must not start while we are in the middle of this function.
   154  	_g_ := getg()
   155  	if _g_.m.locks == 0 && _g_.m.mallocing == 0 && _g_ != _g_.m.g0 {
   156  		throw("MSpan_Sweep: m is not locked")
   157  	}
   158  	sweepgen := mheap_.sweepgen
   159  	if s.state != mSpanInUse || s.sweepgen != sweepgen-1 {
   160  		print("MSpan_Sweep: state=", s.state, " sweepgen=", s.sweepgen, " mheap.sweepgen=", sweepgen, "\n")
   161  		throw("MSpan_Sweep: bad span state")
   162  	}
   163  
   164  	if trace.enabled {
   165  		traceGCSweepStart()
   166  	}
   167  
   168  	xadd64(&mheap_.pagesSwept, int64(s.npages))
   169  
   170  	cl := s.sizeclass
   171  	size := s.elemsize
   172  	res := false
   173  	nfree := 0
   174  
   175  	var head, end gclinkptr
   176  
   177  	c := _g_.m.mcache
   178  	freeToHeap := false
   179  
   180  	// Mark any free objects in this span so we don't collect them.
   181  	sstart := uintptr(s.start << _PageShift)
   182  	for link := s.freelist; link.ptr() != nil; link = link.ptr().next {
   183  		if uintptr(link) < sstart || s.limit <= uintptr(link) {
   184  			// Free list is corrupted.
   185  			dumpFreeList(s)
   186  			throw("free list corrupted")
   187  		}
   188  		heapBitsForAddr(uintptr(link)).setMarkedNonAtomic()
   189  	}
   190  
   191  	// Unlink & free special records for any objects we're about to free.
   192  	specialp := &s.specials
   193  	special := *specialp
   194  	for special != nil {
   195  		// A finalizer can be set for an inner byte of an object, find object beginning.
   196  		p := uintptr(s.start<<_PageShift) + uintptr(special.offset)/size*size
   197  		hbits := heapBitsForAddr(p)
   198  		if !hbits.isMarked() {
   199  			// Find the exact byte for which the special was setup
   200  			// (as opposed to object beginning).
   201  			p := uintptr(s.start<<_PageShift) + uintptr(special.offset)
   202  			// about to free object: splice out special record
   203  			y := special
   204  			special = special.next
   205  			*specialp = special
   206  			if !freespecial(y, unsafe.Pointer(p), size, false) {
   207  				// stop freeing of object if it has a finalizer
   208  				hbits.setMarkedNonAtomic()
   209  			}
   210  		} else {
   211  			// object is still live: keep special record
   212  			specialp = &special.next
   213  			special = *specialp
   214  		}
   215  	}
   216  
   217  	// Sweep through n objects of given size starting at p.
   218  	// This thread owns the span now, so it can manipulate
   219  	// the block bitmap without atomic operations.
   220  
   221  	size, n, _ := s.layout()
   222  	heapBitsSweepSpan(s.base(), size, n, func(p uintptr) {
   223  		// At this point we know that we are looking at garbage object
   224  		// that needs to be collected.
   225  		if debug.allocfreetrace != 0 {
   226  			tracefree(unsafe.Pointer(p), size)
   227  		}
   228  
   229  		// Reset to allocated+noscan.
   230  		if cl == 0 {
   231  			// Free large span.
   232  			if preserve {
   233  				throw("can't preserve large span")
   234  			}
   235  			heapBitsForSpan(p).initSpan(s.layout())
   236  			s.needzero = 1
   237  
   238  			// important to set sweepgen before returning it to heap
   239  			atomicstore(&s.sweepgen, sweepgen)
   240  
   241  			// Free the span after heapBitsSweepSpan
   242  			// returns, since it's not done with the span.
   243  			freeToHeap = true
   244  		} else {
   245  			// Free small object.
   246  			if size > 2*ptrSize {
   247  				*(*uintptr)(unsafe.Pointer(p + ptrSize)) = uintptrMask & 0xdeaddeaddeaddead // mark as "needs to be zeroed"
   248  			} else if size > ptrSize {
   249  				*(*uintptr)(unsafe.Pointer(p + ptrSize)) = 0
   250  			}
   251  			if head.ptr() == nil {
   252  				head = gclinkptr(p)
   253  			} else {
   254  				end.ptr().next = gclinkptr(p)
   255  			}
   256  			end = gclinkptr(p)
   257  			end.ptr().next = gclinkptr(0x0bade5)
   258  			nfree++
   259  		}
   260  	})
   261  
   262  	// We need to set s.sweepgen = h.sweepgen only when all blocks are swept,
   263  	// because of the potential for a concurrent free/SetFinalizer.
   264  	// But we need to set it before we make the span available for allocation
   265  	// (return it to heap or mcentral), because allocation code assumes that a
   266  	// span is already swept if available for allocation.
   267  	//
   268  	// TODO(austin): Clean this up by consolidating atomicstore in
   269  	// large span path above with this.
   270  	if !freeToHeap && nfree == 0 {
   271  		// The span must be in our exclusive ownership until we update sweepgen,
   272  		// check for potential races.
   273  		if s.state != mSpanInUse || s.sweepgen != sweepgen-1 {
   274  			print("MSpan_Sweep: state=", s.state, " sweepgen=", s.sweepgen, " mheap.sweepgen=", sweepgen, "\n")
   275  			throw("MSpan_Sweep: bad span state after sweep")
   276  		}
   277  		atomicstore(&s.sweepgen, sweepgen)
   278  	}
   279  	if nfree > 0 {
   280  		c.local_nsmallfree[cl] += uintptr(nfree)
   281  		res = mCentral_FreeSpan(&mheap_.central[cl].mcentral, s, int32(nfree), head, end, preserve)
   282  		// MCentral_FreeSpan updates sweepgen
   283  	} else if freeToHeap {
   284  		// Free large span to heap
   285  
   286  		// NOTE(rsc,dvyukov): The original implementation of efence
   287  		// in CL 22060046 used SysFree instead of SysFault, so that
   288  		// the operating system would eventually give the memory
   289  		// back to us again, so that an efence program could run
   290  		// longer without running out of memory. Unfortunately,
   291  		// calling SysFree here without any kind of adjustment of the
   292  		// heap data structures means that when the memory does
   293  		// come back to us, we have the wrong metadata for it, either in
   294  		// the MSpan structures or in the garbage collection bitmap.
   295  		// Using SysFault here means that the program will run out of
   296  		// memory fairly quickly in efence mode, but at least it won't
   297  		// have mysterious crashes due to confused memory reuse.
   298  		// It should be possible to switch back to SysFree if we also
   299  		// implement and then call some kind of MHeap_DeleteSpan.
   300  		if debug.efence > 0 {
   301  			s.limit = 0 // prevent mlookup from finding this span
   302  			sysFault(unsafe.Pointer(uintptr(s.start<<_PageShift)), size)
   303  		} else {
   304  			mHeap_Free(&mheap_, s, 1)
   305  		}
   306  		c.local_nlargefree++
   307  		c.local_largefree += size
   308  		res = true
   309  	}
   310  	if trace.enabled {
   311  		traceGCSweepDone()
   312  	}
   313  	return res
   314  }
   315  
   316  // deductSweepCredit deducts sweep credit for allocating a span of
   317  // size spanBytes. This must be performed *before* the span is
   318  // allocated to ensure the system has enough credit. If necessary, it
   319  // performs sweeping to prevent going in to debt. If the caller will
   320  // also sweep pages (e.g., for a large allocation), it can pass a
   321  // non-zero callerSweepPages to leave that many pages unswept.
   322  //
   323  // deductSweepCredit is the core of the "proportional sweep" system.
   324  // It uses statistics gathered by the garbage collector to perform
   325  // enough sweeping so that all pages are swept during the concurrent
   326  // sweep phase between GC cycles.
   327  //
   328  // mheap_ must NOT be locked.
   329  func deductSweepCredit(spanBytes uintptr, callerSweepPages uintptr) {
   330  	if mheap_.sweepPagesPerByte == 0 {
   331  		// Proportional sweep is done or disabled.
   332  		return
   333  	}
   334  
   335  	// Account for this span allocation.
   336  	spanBytesAlloc := xadd64(&mheap_.spanBytesAlloc, int64(spanBytes))
   337  
   338  	// Fix debt if necessary.
   339  	pagesOwed := int64(mheap_.sweepPagesPerByte * float64(spanBytesAlloc))
   340  	for pagesOwed-int64(atomicload64(&mheap_.pagesSwept)) > int64(callerSweepPages) {
   341  		if gosweepone() == ^uintptr(0) {
   342  			mheap_.sweepPagesPerByte = 0
   343  			break
   344  		}
   345  	}
   346  }
   347  
   348  func dumpFreeList(s *mspan) {
   349  	printlock()
   350  	print("runtime: free list of span ", s, ":\n")
   351  	sstart := uintptr(s.start << _PageShift)
   352  	link := s.freelist
   353  	for i := 0; i < int(s.npages*_PageSize/s.elemsize); i++ {
   354  		if i != 0 {
   355  			print(" -> ")
   356  		}
   357  		print(hex(link))
   358  		if link.ptr() == nil {
   359  			break
   360  		}
   361  		if uintptr(link) < sstart || s.limit <= uintptr(link) {
   362  			// Bad link. Stop walking before we crash.
   363  			print(" (BAD)")
   364  			break
   365  		}
   366  		link = link.ptr().next
   367  	}
   368  	print("\n")
   369  	printunlock()
   370  }