@@ -180,7 +180,13 @@ func gcinit() {
180
180
datap .gcdatamask = progToPointerMask ((* byte )(unsafe .Pointer (datap .gcdata )), datap .edata - datap .data )
181
181
datap .gcbssmask = progToPointerMask ((* byte )(unsafe .Pointer (datap .gcbss )), datap .ebss - datap .bss )
182
182
}
183
- memstats .next_gc = heapminimum
183
+ memstats .gc_trigger = heapminimum
184
+ // Compute the goal heap size based on the trigger:
185
+ // trigger = marked * (1 + triggerRatio)
186
+ // marked = trigger / (1 + triggerRatio)
187
+ // goal = marked * (1 + GOGC/100)
188
+ // = trigger / (1 + triggerRatio) * (1 + GOGC/100)
189
+ memstats .next_gc = uint64 (float64 (memstats .gc_trigger ) / (1 + gcController .triggerRatio ) * (1 + float64 (gcpercent )/ 100 ))
184
190
work .startSema = 1
185
191
work .markDoneSema = 1
186
192
}
@@ -218,6 +224,9 @@ func setGCPercent(in int32) (out int32) {
218
224
if gcController .triggerRatio > float64 (gcpercent )/ 100 {
219
225
gcController .triggerRatio = float64 (gcpercent ) / 100
220
226
}
227
+ // This is either in gcinit or followed by a STW GC, both of
228
+ // which will reset other stats like memstats.gc_trigger and
229
+ // memstats.next_gc to appropriate values.
221
230
unlock (& mheap_ .lock )
222
231
return out
223
232
}
@@ -303,7 +312,7 @@ const (
303
312
// when to trigger concurrent garbage collection and how much marking
304
313
// work to do in mutator assists and background marking.
305
314
//
306
- // It uses a feedback control algorithm to adjust the memstats.next_gc
315
+ // It uses a feedback control algorithm to adjust the memstats.gc_trigger
307
316
// trigger based on the heap growth and GC CPU utilization each cycle.
308
317
// This algorithm optimizes for heap growth to match GOGC and for CPU
309
318
// utilization between assist and background marking to be 25% of
@@ -359,10 +368,6 @@ type gcControllerState struct {
359
368
// that assists and background mark workers started.
360
369
markStartTime int64
361
370
362
- // heapGoal is the goal memstats.heap_live for when this cycle
363
- // ends. This is computed at the beginning of each cycle.
364
- heapGoal uint64
365
-
366
371
// dedicatedMarkWorkersNeeded is the number of dedicated mark
367
372
// workers that need to be started. This is computed at the
368
373
// beginning of each cycle and decremented atomically as
@@ -390,8 +395,9 @@ type gcControllerState struct {
390
395
// triggerRatio is the heap growth ratio at which the garbage
391
396
// collection cycle should start. E.g., if this is 0.6, then
392
397
// GC should start when the live heap has reached 1.6 times
393
- // the heap size marked by the previous cycle. This is updated
394
- // at the end of of each cycle.
398
+ // the heap size marked by the previous cycle. This should be
399
+ // ≤ GOGC/100 so the trigger heap size is less than the goal
400
+ // heap size. This is updated at the end of of each cycle.
395
401
triggerRatio float64
396
402
397
403
_ [sys .CacheLineSize ]byte
@@ -416,28 +422,29 @@ func (c *gcControllerState) startCycle() {
416
422
c .idleMarkTime = 0
417
423
418
424
// If this is the first GC cycle or we're operating on a very
419
- // small heap, fake heap_marked so it looks like next_gc is
425
+ // small heap, fake heap_marked so it looks like gc_trigger is
420
426
// the appropriate growth from heap_marked, even though the
421
427
// real heap_marked may not have a meaningful value (on the
422
428
// first cycle) or may be much smaller (resulting in a large
423
429
// error response).
424
- if memstats .next_gc <= heapminimum {
425
- memstats .heap_marked = uint64 (float64 (memstats .next_gc ) / (1 + c .triggerRatio ))
430
+ if memstats .gc_trigger <= heapminimum {
431
+ memstats .heap_marked = uint64 (float64 (memstats .gc_trigger ) / (1 + c .triggerRatio ))
426
432
memstats .heap_reachable = memstats .heap_marked
427
433
}
428
434
429
- // Compute the heap goal for this cycle
430
- c .heapGoal = memstats .heap_reachable + memstats .heap_reachable * uint64 (gcpercent )/ 100
435
+ // Re-compute the heap goal for this cycle in case something
436
+ // changed. This is the same calculation we use elsewhere.
437
+ memstats .next_gc = memstats .heap_reachable + memstats .heap_reachable * uint64 (gcpercent )/ 100
431
438
432
439
// Ensure that the heap goal is at least a little larger than
433
440
// the current live heap size. This may not be the case if GC
434
441
// start is delayed or if the allocation that pushed heap_live
435
- // over next_gc is large or if the trigger is really close to
442
+ // over gc_trigger is large or if the trigger is really close to
436
443
// GOGC. Assist is proportional to this distance, so enforce a
437
444
// minimum distance, even if it means going over the GOGC goal
438
445
// by a tiny bit.
439
- if c . heapGoal < memstats .heap_live + 1024 * 1024 {
440
- c . heapGoal = memstats .heap_live + 1024 * 1024
446
+ if memstats . next_gc < memstats .heap_live + 1024 * 1024 {
447
+ memstats . next_gc = memstats .heap_live + 1024 * 1024
441
448
}
442
449
443
450
// Compute the total mark utilization goal and divide it among
@@ -467,7 +474,7 @@ func (c *gcControllerState) startCycle() {
467
474
print ("pacer: assist ratio=" , c .assistWorkPerByte ,
468
475
" (scan " , memstats .heap_scan >> 20 , " MB in " ,
469
476
work .initialHeapLive >> 20 , "->" ,
470
- c . heapGoal >> 20 , " MB)" ,
477
+ memstats . next_gc >> 20 , " MB)" ,
471
478
" workers=" , c .dedicatedMarkWorkersNeeded ,
472
479
"+" , c .fractionalMarkWorkersNeeded , "\n " )
473
480
}
@@ -516,7 +523,7 @@ func (c *gcControllerState) revise() {
516
523
}
517
524
518
525
// Compute the heap distance remaining.
519
- heapDistance := int64 (c . heapGoal ) - int64 (memstats .heap_live )
526
+ heapDistance := int64 (memstats . next_gc ) - int64 (memstats .heap_live )
520
527
if heapDistance <= 0 {
521
528
// This shouldn't happen, but if it does, avoid
522
529
// dividing by zero or setting the assist negative.
@@ -552,7 +559,7 @@ func (c *gcControllerState) endCycle() {
552
559
// difference between this estimate and the GOGC-based goal
553
560
// heap growth is the error.
554
561
//
555
- // TODO(austin): next_gc is based on heap_reachable, not
562
+ // TODO(austin): gc_trigger is based on heap_reachable, not
556
563
// heap_marked, which means the actual growth ratio
557
564
// technically isn't comparable to the trigger ratio.
558
565
goalGrowthRatio := float64 (gcpercent ) / 100
@@ -585,7 +592,7 @@ func (c *gcControllerState) endCycle() {
585
592
// Print controller state in terms of the design
586
593
// document.
587
594
H_m_prev := memstats .heap_marked
588
- H_T := memstats .next_gc
595
+ H_T := memstats .gc_trigger
589
596
h_a := actualGrowthRatio
590
597
H_a := memstats .heap_live
591
598
h_g := goalGrowthRatio
@@ -881,7 +888,7 @@ const (
881
888
// If forceTrigger is true, it ignores the current heap size, but
882
889
// checks all other conditions. In general this should be false.
883
890
func gcShouldStart (forceTrigger bool ) bool {
884
- return gcphase == _GCoff && (forceTrigger || memstats .heap_live >= memstats .next_gc ) && memstats .enablegc && panicking == 0 && gcpercent >= 0
891
+ return gcphase == _GCoff && (forceTrigger || memstats .heap_live >= memstats .gc_trigger ) && memstats .enablegc && panicking == 0 && gcpercent >= 0
885
892
}
886
893
887
894
// gcStart transitions the GC from _GCoff to _GCmark (if mode ==
@@ -979,7 +986,7 @@ func gcStart(mode gcMode, forceTrigger bool) {
979
986
980
987
if mode == gcBackgroundMode { // Do as much work concurrently as possible
981
988
gcController .startCycle ()
982
- work .heapGoal = gcController . heapGoal
989
+ work .heapGoal = memstats . next_gc
983
990
984
991
// Enter concurrent mark phase and enable
985
992
// write barriers.
@@ -1624,13 +1631,13 @@ func gcMark(start_time int64) {
1624
1631
// by triggerRatio over the reachable heap size. Assume that
1625
1632
// we're in steady state, so the reachable heap size is the
1626
1633
// same now as it was at the beginning of the GC cycle.
1627
- memstats .next_gc = uint64 (float64 (memstats .heap_reachable ) * (1 + gcController .triggerRatio ))
1628
- if memstats .next_gc < heapminimum {
1629
- memstats .next_gc = heapminimum
1634
+ memstats .gc_trigger = uint64 (float64 (memstats .heap_reachable ) * (1 + gcController .triggerRatio ))
1635
+ if memstats .gc_trigger < heapminimum {
1636
+ memstats .gc_trigger = heapminimum
1630
1637
}
1631
- if int64 (memstats .next_gc ) < 0 {
1638
+ if int64 (memstats .gc_trigger ) < 0 {
1632
1639
print ("next_gc=" , memstats .next_gc , " bytesMarked=" , work .bytesMarked , " heap_live=" , memstats .heap_live , " initialHeapLive=" , work .initialHeapLive , "\n " )
1633
- throw ("next_gc underflow" )
1640
+ throw ("gc_trigger underflow" )
1634
1641
}
1635
1642
1636
1643
// Update other GC heap size stats. This must happen after
@@ -1640,19 +1647,26 @@ func gcMark(start_time int64) {
1640
1647
memstats .heap_marked = work .bytesMarked
1641
1648
memstats .heap_scan = uint64 (gcController .scanWork )
1642
1649
1643
- minNextGC := memstats .heap_live + sweepMinHeapDistance * uint64 (gcpercent )/ 100
1644
- if memstats .next_gc < minNextGC {
1650
+ minTrigger := memstats .heap_live + sweepMinHeapDistance * uint64 (gcpercent )/ 100
1651
+ if memstats .gc_trigger < minTrigger {
1645
1652
// The allocated heap is already past the trigger.
1646
1653
// This can happen if the triggerRatio is very low and
1647
1654
// the reachable heap estimate is less than the live
1648
1655
// heap size.
1649
1656
//
1650
1657
// Concurrent sweep happens in the heap growth from
1651
- // heap_live to next_gc , so bump next_gc up to ensure
1658
+ // heap_live to gc_trigger , so bump gc_trigger up to ensure
1652
1659
// that concurrent sweep has some heap growth in which
1653
1660
// to perform sweeping before we start the next GC
1654
1661
// cycle.
1655
- memstats .next_gc = minNextGC
1662
+ memstats .gc_trigger = minTrigger
1663
+ }
1664
+
1665
+ // The next GC cycle should finish before the allocated heap
1666
+ // has grown by GOGC/100.
1667
+ memstats .next_gc = memstats .heap_reachable + memstats .heap_reachable * uint64 (gcpercent )/ 100
1668
+ if memstats .next_gc < memstats .gc_trigger {
1669
+ memstats .next_gc = memstats .gc_trigger
1656
1670
}
1657
1671
1658
1672
if trace .enabled {
@@ -1693,7 +1707,7 @@ func gcSweep(mode gcMode) {
1693
1707
// Concurrent sweep needs to sweep all of the in-use pages by
1694
1708
// the time the allocated heap reaches the GC trigger. Compute
1695
1709
// the ratio of in-use pages to sweep per byte allocated.
1696
- heapDistance := int64 (memstats .next_gc ) - int64 (memstats .heap_live )
1710
+ heapDistance := int64 (memstats .gc_trigger ) - int64 (memstats .heap_live )
1697
1711
// Add a little margin so rounding errors and concurrent
1698
1712
// sweep are less likely to leave pages unswept when GC starts.
1699
1713
heapDistance -= 1024 * 1024
0 commit comments