标记

垃圾回收的代码主要集中在函数 gcStart() 中。

  1. // gcStart 是 GC 的入口函数,根据 gcMode 做处理。
  2. // 1. gcMode == gcBackgroundMode(后台运行,也就是并行), _GCoff -> _GCmark
  3. // 2. 否则 GCoff -> _GCmarktermination,这个时候就是主动 GC
  4. func gcStart(mode gcMode, forceTrigger bool) {
  5. ...
  6. }
  • STW phase 1

在 GC 开始之前的准备工作。

  1. func gcStart(mode gcMode, forceTrigger bool) {
  2. ...
  3. //在后台启动 mark worker
  4. if mode == gcBackgroundMode {
  5. gcBgMarkStartWorkers()
  6. }
  7. ...
  8. // Stop The World
  9. systemstack(stopTheWorldWithSema)
  10. ...
  11. if mode == gcBackgroundMode {
  12. // GC 开始前的准备工作
  13. //处理设置 GCPhase,setGCPhase 还会 enable write barrier
  14. setGCPhase(_GCmark)
  15. gcBgMarkPrepare() // Must happen before assist enable.
  16. gcMarkRootPrepare()
  17. // Mark all active tinyalloc blocks. Since we're
  18. // allocating from these, they need to be black like
  19. // other allocations. The alternative is to blacken
  20. // the tiny block on every allocation from it, which
  21. // would slow down the tiny allocator.
  22. gcMarkTinyAllocs()
  23. // Start The World
  24. systemstack(startTheWorldWithSema)
  25. } else {
  26. ...
  27. }
  28. }
  • Mark

Mark 阶段是并行的运行,通过在后台一直运行 mark worker 来实现。

  1. func gcStart(mode gcMode, forceTrigger bool) {
  2. ...
  3. //在后台启动 mark worker
  4. if mode == gcBackgroundMode {
  5. gcBgMarkStartWorkers()
  6. }
  7. }
  8. func gcBgMarkStartWorkers() {
  9. // Background marking is performed by per-P G's. Ensure that
  10. // each P has a background GC G.
  11. for _, p := range &allp {
  12. if p == nil || p.status == _Pdead {
  13. break
  14. }
  15. if p.gcBgMarkWorker == 0 {
  16. go gcBgMarkWorker(p)
  17. notetsleepg(&work.bgMarkReady, -1)
  18. noteclear(&work.bgMarkReady)
  19. }
  20. }
  21. }
  22. // gcBgMarkWorker 是一直在后台运行的,大部分时候是休眠状态,通过 gcController 来调度
  23. func gcBgMarkWorker(_p_ *p) {
  24. for {
  25. // 将当前 goroutine 休眠,直到满足某些条件
  26. gopark(...)
  27. ...
  28. // mark 过程
  29. systemstack(func() {
  30. // Mark our goroutine preemptible so its stack
  31. // can be scanned. This lets two mark workers
  32. // scan each other (otherwise, they would
  33. // deadlock). We must not modify anything on
  34. // the G stack. However, stack shrinking is
  35. // disabled for mark workers, so it is safe to
  36. // read from the G stack.
  37. casgstatus(gp, _Grunning, _Gwaiting)
  38. switch _p_.gcMarkWorkerMode {
  39. default:
  40. throw("gcBgMarkWorker: unexpected gcMarkWorkerMode")
  41. case gcMarkWorkerDedicatedMode:
  42. gcDrain(&_p_.gcw, gcDrainNoBlock|gcDrainFlushBgCredit)
  43. case gcMarkWorkerFractionalMode:
  44. gcDrain(&_p_.gcw, gcDrainUntilPreempt|gcDrainFlushBgCredit)
  45. case gcMarkWorkerIdleMode:
  46. gcDrain(&_p_.gcw, gcDrainIdle|gcDrainUntilPreempt|gcDrainFlushBgCredit)
  47. }
  48. casgstatus(gp, _Gwaiting, _Grunning)
  49. })
  50. ...
  51. }
  52. }

Mark 阶段的标记代码主要在函数 gcDrain() 中实现。

  1. // gcDrain scans roots and objects in work buffers, blackening grey
  2. // objects until all roots and work buffers have been drained.
  3. func gcDrain(gcw *gcWork, flags gcDrainFlags) {
  4. ...
  5. // Drain root marking jobs.
  6. if work.markrootNext < work.markrootJobs {
  7. for !(preemptible && gp.preempt) {
  8. job := atomic.Xadd(&work.markrootNext, +1) - 1
  9. if job >= work.markrootJobs {
  10. break
  11. }
  12. markroot(gcw, job)
  13. if idle && pollWork() {
  14. goto done
  15. }
  16. }
  17. }
  18. // 处理 heap 标记
  19. // Drain heap marking jobs.
  20. for !(preemptible && gp.preempt) {
  21. ...
  22. //从灰色列队中取出对象
  23. var b uintptr
  24. if blocking {
  25. b = gcw.get()
  26. } else {
  27. b = gcw.tryGetFast()
  28. if b == 0 {
  29. b = gcw.tryGet()
  30. }
  31. }
  32. if b == 0 {
  33. // work barrier reached or tryGet failed.
  34. break
  35. }
  36. //扫描灰色对象的引用对象,标记为灰色,入灰色队列
  37. scanobject(b, gcw)
  38. }
  39. }
  • Mark termination (STW phase 2)

mark termination 阶段会 stop the world。函数实现在 gcMarkTermination()。

  1. func gcMarkTermination() {
  2. // World is stopped.
  3. // Run gc on the g0 stack. We do this so that the g stack
  4. // we're currently running on will no longer change. Cuts
  5. // the root set down a bit (g0 stacks are not scanned, and
  6. // we don't need to scan gc's internal state). We also
  7. // need to switch to g0 so we can shrink the stack.
  8. systemstack(func() {
  9. gcMark(startTime)
  10. // Must return immediately.
  11. // The outer function's stack may have moved
  12. // during gcMark (it shrinks stacks, including the
  13. // outer function's stack), so we must not refer
  14. // to any of its variables. Return back to the
  15. // non-system stack to pick up the new addresses
  16. // before continuing.
  17. })
  18. ...
  19. }