@@ -85,7 +85,7 @@ func setGCPercent(in int32) (out int32) {
8585}
8686```
8787
88- ## gcStart
88+ ## gc 的调用时机
8989
9090整个 gc 流程的入口是 gcStart,gcStart 的调用方为:
9191
@@ -105,7 +105,286 @@ forcegchelper --> gcStart
105105
106106* mallocgc,分配堆内存时触发,会检查当前是否满足触发 gc 的条件,如果触发,那么进入 gcStart。
107107* forcegchelper,在 forcegchelper 中会把 forcegc.g 这个全局对象的运行 g 挂起。sysmon 会调用 test 检查上次触发 gc 的时间到当前时间是否已经经过了 forcegcperiod 长的时间,如果已经超过,那么就会将 forcegc.g 注入到 globrunq。这样会在该 g 被调度到的时候触发 gc。
108- * runtime.GC 是由用户主动触发的。
108+ * runtime.GC 是由用户主动触发的,相当于强制触发 GC。
109+
110+ ## gcTrigger 和 gc 条件检查
111+
112+ ``` go
113+ // A gcTrigger is a predicate for starting a GC cycle. Specifically,
114+ // it is an exit condition for the _GCoff phase.
115+ type gcTrigger struct {
116+ kind gcTriggerKind
117+ now int64 // gcTriggerTime: current time
118+ n uint32 // gcTriggerCycle: cycle number to start
119+ }
120+
121+ type gcTriggerKind int
122+
123+ const (
124+ // gcTriggerAlways indicates that a cycle should be started
125+ // unconditionally, even if GOGC is off or we're in a cycle
126+ // right now. This cannot be consolidated with other cycles.
127+ gcTriggerAlways gcTriggerKind = iota
128+
129+ // gcTriggerHeap indicates that a cycle should be started when
130+ // the heap size reaches the trigger heap size computed by the
131+ // controller.
132+ gcTriggerHeap
133+
134+ // gcTriggerTime indicates that a cycle should be started when
135+ // it's been more than forcegcperiod nanoseconds since the
136+ // previous GC cycle.
137+ gcTriggerTime
138+
139+ // gcTriggerCycle indicates that a cycle should be started if
140+ // we have not yet started cycle number gcTrigger.n (relative
141+ // to work.cycles).
142+ gcTriggerCycle
143+ )
144+ ```
145+
146+ ### mallocgc 中的 trigger 类型是 gcTriggerHeap;
147+
148+ ``` go
149+
150+ if shouldhelpgc {
151+ if t := (gcTrigger{kind: gcTriggerHeap}); t.test () {
152+ gcStart (gcBackgroundMode, t)
153+ }
154+ }
155+ ```
156+
157+ ### runtime.GC 中使用的是 gcTriggerCycle;
158+
159+ ``` go
160+ // We're now in sweep N or later. Trigger GC cycle N+1, which
161+ // will first finish sweep N if necessary and then enter sweep
162+ // termination N+1.
163+ gcStart (gcBackgroundMode, gcTrigger{kind: gcTriggerCycle, n: n + 1 })
164+ ```
165+
166+ ### forcegchelper 中使用的是 gcTriggerTime;
167+
168+ ``` go
169+ // Time-triggered, fully concurrent.
170+ gcStart (gcBackgroundMode, gcTrigger{kind: gcTriggerTime, now: nanotime ()})
171+ ```
172+
173+ ### sysmon 中检查时使用的也是 gcTriggerTime
174+
175+ 这里和前面三条不一样的是,这种情况下如果 trigger.test 返回 true,会使用 forcegchelper 所在的 g 来执行 gcStart,具体做法就是上面提到的把 forcegc.g 注入到全局 runq;
176+
177+ ``` go
178+ // check if we need to force a GC
179+ if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test () && atomic.Load (&forcegc.idle ) != 0 {
180+ lock (&forcegc.lock )
181+ forcegc.idle = 0
182+ forcegc.g .schedlink = 0
183+ injectglist (forcegc.g )
184+ unlock (&forcegc.lock )
185+ }
186+ ```
187+
188+ ### trigger.test, gc 条件检查
189+
190+ ``` go
191+ // test returns true if the trigger condition is satisfied, meaning
192+ // that the exit condition for the _GCoff phase has been met. The exit
193+ // condition should be tested when allocating.
194+ func (t gcTrigger ) test () bool {
195+ if !memstats.enablegc || panicking != 0 {
196+ return false
197+ }
198+ if t.kind == gcTriggerAlways {
199+ return true
200+ }
201+ if gcphase != _GCoff {
202+ return false
203+ }
204+ switch t.kind {
205+ case gcTriggerHeap:
206+ // Non-atomic access to heap_live for performance. If
207+ // we are going to trigger on this, this thread just
208+ // atomically wrote heap_live anyway and we'll see our
209+ // own write.
210+ return memstats.heap_live >= memstats.gc_trigger
211+ case gcTriggerTime:
212+ if gcpercent < 0 {
213+ return false
214+ }
215+ lastgc := int64 (atomic.Load64 (&memstats.last_gc_nanotime ))
216+ return lastgc != 0 && t.now -lastgc > forcegcperiod
217+ case gcTriggerCycle:
218+ // t.n > work.cycles, but accounting for wraparound.
219+ return int32 (t.n -work.cycles ) > 0
220+ }
221+ return true
222+ }
223+ ```
224+
225+ ## gcStart
226+
227+ ``` go
228+ // gcStart transitions the GC from _GCoff to _GCmark (if
229+ // !mode.stwMark) or _GCmarktermination (if mode.stwMark) by
230+ // performing sweep termination and GC initialization.
231+ //
232+ // This may return without performing this transition in some cases,
233+ // such as when called on a system stack or with locks held.
234+ func gcStart (mode gcMode , trigger gcTrigger ) {
235+ // Since this is called from malloc and malloc is called in
236+ // the guts of a number of libraries that might be holding
237+ // locks, don't attempt to start GC in non-preemptible or
238+ // potentially unstable situations.
239+ mp := acquirem ()
240+ if gp := getg (); gp == mp.g0 || mp.locks > 1 || mp.preemptoff != " " {
241+ releasem (mp)
242+ return
243+ }
244+ releasem (mp)
245+ mp = nil
246+
247+ // Pick up the remaining unswept/not being swept spans concurrently
248+ //
249+ // This shouldn't happen if we're being invoked in background
250+ // mode since proportional sweep should have just finished
251+ // sweeping everything, but rounding errors, etc, may leave a
252+ // few spans unswept. In forced mode, this is necessary since
253+ // GC can be forced at any point in the sweeping cycle.
254+ //
255+ // We check the transition condition continuously here in case
256+ // this G gets delayed in to the next GC cycle.
257+ for trigger.test () && gosweepone () != ^uintptr (0 ) {
258+ sweep.nbgsweep ++
259+ }
260+
261+ // Perform GC initialization and the sweep termination
262+ // transition.
263+ semacquire (&work.startSema )
264+ // Re-check transition condition under transition lock.
265+ if !trigger.test () {
266+ semrelease (&work.startSema )
267+ return
268+ }
269+
270+ // For stats, check if this GC was forced by the user.
271+ work.userForced = trigger.kind == gcTriggerAlways || trigger.kind == gcTriggerCycle
272+
273+ // In gcstoptheworld debug mode, upgrade the mode accordingly.
274+ // We do this after re-checking the transition condition so
275+ // that multiple goroutines that detect the heap trigger don't
276+ // start multiple STW GCs.
277+ if mode == gcBackgroundMode {
278+ if debug.gcstoptheworld == 1 {
279+ mode = gcForceMode
280+ } else if debug.gcstoptheworld == 2 {
281+ mode = gcForceBlockMode
282+ }
283+ }
284+
285+ // Ok, we're doing it! Stop everybody else
286+ semacquire (&worldsema)
287+
288+ if trace.enabled {
289+ traceGCStart ()
290+ }
291+
292+ if mode == gcBackgroundMode {
293+ gcBgMarkStartWorkers ()
294+ }
295+
296+ gcResetMarkState ()
297+
298+ work.stwprocs , work.maxprocs = gomaxprocs, gomaxprocs
299+ if work.stwprocs > ncpu {
300+ // This is used to compute CPU time of the STW phases,
301+ // so it can't be more than ncpu, even if GOMAXPROCS is.
302+ work.stwprocs = ncpu
303+ }
304+ work.heap0 = atomic.Load64 (&memstats.heap_live )
305+ work.pauseNS = 0
306+ work.mode = mode
307+
308+ now := nanotime ()
309+ work.tSweepTerm = now
310+ work.pauseStart = now
311+ if trace.enabled {
312+ traceGCSTWStart (1 )
313+ }
314+ systemstack (stopTheWorldWithSema)
315+ // Finish sweep before we start concurrent scan.
316+ systemstack (func () {
317+ finishsweep_m ()
318+ })
319+ // clearpools before we start the GC. If we wait they memory will not be
320+ // reclaimed until the next GC cycle.
321+ clearpools ()
322+
323+ work.cycles ++
324+ if mode == gcBackgroundMode { // Do as much work concurrently as possible
325+ gcController.startCycle ()
326+ work.heapGoal = memstats.next_gc
327+
328+ // Enter concurrent mark phase and enable
329+ // write barriers.
330+ //
331+ // Because the world is stopped, all Ps will
332+ // observe that write barriers are enabled by
333+ // the time we start the world and begin
334+ // scanning.
335+ //
336+ // Write barriers must be enabled before assists are
337+ // enabled because they must be enabled before
338+ // any non-leaf heap objects are marked. Since
339+ // allocations are blocked until assists can
340+ // happen, we want enable assists as early as
341+ // possible.
342+ setGCPhase (_GCmark)
343+
344+ gcBgMarkPrepare () // Must happen before assist enable.
345+ gcMarkRootPrepare ()
346+
347+ // Mark all active tinyalloc blocks. Since we're
348+ // allocating from these, they need to be black like
349+ // other allocations. The alternative is to blacken
350+ // the tiny block on every allocation from it, which
351+ // would slow down the tiny allocator.
352+ gcMarkTinyAllocs ()
353+
354+ // At this point all Ps have enabled the write
355+ // barrier, thus maintaining the no white to
356+ // black invariant. Enable mutator assists to
357+ // put back-pressure on fast allocating
358+ // mutators.
359+ atomic.Store (&gcBlackenEnabled, 1 )
360+
361+ // Assists and workers can start the moment we start
362+ // the world.
363+ gcController.markStartTime = now
364+
365+ // Concurrent mark.
366+ systemstack (func () {
367+ now = startTheWorldWithSema (trace.enabled )
368+ })
369+ work.pauseNS += now - work.pauseStart
370+ work.tMark = now
371+ } else {
372+ if trace.enabled {
373+ // Switch to mark termination STW.
374+ traceGCSTWDone ()
375+ traceGCSTWStart (0 )
376+ }
377+ t := nanotime ()
378+ work.tMark , work.tMarkTerm = t, t
379+ work.heapGoal = work.heap0
380+
381+ // Perform mark termination. This will restart the world.
382+ gcMarkTermination (memstats.triggerRatio )
383+ }
384+
385+ semrelease (&work.startSema )
386+ }
387+ ```
109388
110389## runtime.GC
111390
@@ -203,4 +482,8 @@ func GC() {
203482 }
204483 releasem (mp)
205484}
206- ```
485+ ```
486+
487+ ## FAQ
488+
489+ 为什么需要 debug.FreeOsMemory 才能释放堆空间。
0 commit comments