@@ -329,9 +329,9 @@ type mspan struct {
329
329
needzero uint8 // needs to be zeroed before allocation
330
330
divShift uint8 // for divide by elemsize - divMagic.shift
331
331
divShift2 uint8 // for divide by elemsize - divMagic.shift2
332
+ scavenged bool // whether this span has had its pages released to the OS
332
333
elemsize uintptr // computed from sizeclass or from npages
333
334
unusedsince int64 // first time spotted by gc in mspanfree state
334
- npreleased uintptr // number of pages released to the os
335
335
limit uintptr // end of data in span
336
336
speciallock mutex // guards specials list
337
337
specials * special // linked list of special records sorted by offset.
@@ -350,34 +350,45 @@ func (s *mspan) layout() (size, n, total uintptr) {
350
350
return
351
351
}
352
352
353
- func (s * mspan ) scavenge () uintptr {
353
+ // physPageBounds returns the start and end of the span
354
+ // rounded in to the physical page size.
355
+ func (s * mspan ) physPageBounds () (uintptr , uintptr ) {
354
356
start := s .base ()
355
357
end := start + s .npages << _PageShift
356
358
if physPageSize > _PageSize {
357
- // We can only release pages in
358
- // physPageSize blocks, so round start
359
- // and end in. (Otherwise, madvise
360
- // will round them *out* and release
361
- // more memory than we want.)
359
+ // Round start and end in.
362
360
start = (start + physPageSize - 1 ) &^ (physPageSize - 1 )
363
361
end &^= physPageSize - 1
364
- if end <= start {
365
- // start and end don't span a
366
- // whole physical page.
367
- return 0
368
- }
369
362
}
370
- len := end - start
371
- released := len - (s .npreleased << _PageShift )
372
- if physPageSize > _PageSize && released == 0 {
363
+ return start , end
364
+ }
365
+
366
+ func (s * mspan ) scavenge () uintptr {
367
+ // start and end must be rounded in, otherwise madvise
368
+ // will round them *out* and release more memory
369
+ // than we want.
370
+ start , end := s .physPageBounds ()
371
+ if end <= start {
372
+ // start and end don't span a whole physical page.
373
373
return 0
374
374
}
375
+ released := end - start
375
376
memstats .heap_released += uint64 (released )
376
- s .npreleased = len >> _PageShift
377
- sysUnused (unsafe .Pointer (start ), len )
377
+ s .scavenged = true
378
+ sysUnused (unsafe .Pointer (start ), released )
378
379
return released
379
380
}
380
381
382
+ // released returns the number of bytes in this span
383
+ // which were returned back to the OS.
384
+ func (s * mspan ) released () uintptr {
385
+ if ! s .scavenged {
386
+ return 0
387
+ }
388
+ start , end := s .physPageBounds ()
389
+ return end - start
390
+ }
391
+
381
392
// recordspan adds a newly allocated span to h.allspans.
382
393
//
383
394
// This only happens the first time a span is allocated from
@@ -873,10 +884,18 @@ HaveSpan:
873
884
if s .npages < npage {
874
885
throw ("MHeap_AllocLocked - bad npages" )
875
886
}
876
- if s .npreleased > 0 {
887
+ if s .scavenged {
888
+ // sysUsed all the pages that are actually available
889
+ // in the span, but only drop heap_released by the
890
+ // actual amount of pages released. This helps ensure
891
+ // that heap_released only increments and decrements
892
+ // by the same amounts. It's also fine, because any
893
+ // of the pages outside start and end wouldn't have been
894
+ // sysUnused in the first place.
877
895
sysUsed (unsafe .Pointer (s .base ()), s .npages << _PageShift )
878
- memstats .heap_released -= uint64 (s .npreleased << _PageShift )
879
- s .npreleased = 0
896
+ start , end := s .physPageBounds ()
897
+ memstats .heap_released -= uint64 (end - start )
898
+ s .scavenged = false
880
899
}
881
900
882
901
if s .npages > npage {
@@ -1019,8 +1038,8 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
1019
1038
1020
1039
// We scavenge s at the end after coalescing if s or anything
1021
1040
// it merged with is marked scavenged.
1022
- needsScavenge := s .npreleased != 0
1023
- prescavenged := s .npreleased * pageSize // number of bytes already scavenged.
1041
+ needsScavenge := s .scavenged
1042
+ prescavenged := s .released () // number of bytes already scavenged.
1024
1043
1025
1044
// Coalesce with earlier, later spans.
1026
1045
if before := spanOf (s .base () - 1 ); before != nil && before .state == mSpanFree {
@@ -1029,15 +1048,14 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
1029
1048
s .npages += before .npages
1030
1049
s .needzero |= before .needzero
1031
1050
h .setSpan (before .base (), s )
1032
- s .npreleased += before .npreleased // absorb released pages
1033
1051
// The size is potentially changing so the treap needs to delete adjacent nodes and
1034
1052
// insert back as a combined node.
1035
- if before .npreleased == 0 {
1053
+ if ! before .scavenged {
1036
1054
h .free .removeSpan (before )
1037
1055
} else {
1038
1056
h .scav .removeSpan (before )
1039
1057
needsScavenge = true
1040
- prescavenged += before .npreleased * pageSize
1058
+ prescavenged += before .released ()
1041
1059
}
1042
1060
before .state = mSpanDead
1043
1061
h .spanalloc .free (unsafe .Pointer (before ))
@@ -1048,14 +1066,13 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
1048
1066
s .npages += after .npages
1049
1067
s .needzero |= after .needzero
1050
1068
h .setSpan (s .base ()+ s .npages * pageSize - 1 , s )
1051
- if after .npreleased == 0 {
1069
+ if ! after .scavenged {
1052
1070
h .free .removeSpan (after )
1053
1071
} else {
1054
1072
h .scav .removeSpan (after )
1055
1073
needsScavenge = true
1056
- prescavenged += after .npreleased * pageSize
1074
+ prescavenged += after .released ()
1057
1075
}
1058
- s .npreleased += after .npreleased
1059
1076
after .state = mSpanDead
1060
1077
h .spanalloc .free (unsafe .Pointer (after ))
1061
1078
}
@@ -1076,7 +1093,7 @@ func (h *mheap) freeSpanLocked(s *mspan, acctinuse, acctidle bool, unusedsince i
1076
1093
}
1077
1094
1078
1095
// Insert s into the appropriate treap.
1079
- if s .npreleased != 0 {
1096
+ if s .scavenged {
1080
1097
h .scav .insert (s )
1081
1098
} else {
1082
1099
h .free .insert (s )
@@ -1157,7 +1174,7 @@ func (span *mspan) init(base uintptr, npages uintptr) {
1157
1174
span .elemsize = 0
1158
1175
span .state = mSpanDead
1159
1176
span .unusedsince = 0
1160
- span .npreleased = 0
1177
+ span .scavenged = false
1161
1178
span .speciallock .key = 0
1162
1179
span .specials = nil
1163
1180
span .needzero = 0
0 commit comments