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