]> Cypherpunks.ru repositories - gostls13.git/commitdiff
runtime: move inconsistent memstats into gcController
authorMichael Anthony Knyszek <mknyszek@google.com>
Fri, 1 Apr 2022 22:34:45 +0000 (22:34 +0000)
committerMichael Knyszek <mknyszek@google.com>
Tue, 3 May 2022 15:12:38 +0000 (15:12 +0000)
Fundamentally, all of these memstats exist to serve the runtime in
managing memory. For the sake of simpler testing, couple these stats
more tightly with the GC.

This CL was mostly done automatically. The fields had to be moved
manually, but the references to the fields were updated via

    gofmt -w -r 'memstats.<field> -> gcController.<field>' *.go

For #48409.

Change-Id: Ic036e875c98138d9a11e1c35f8c61b784c376134
Reviewed-on: https://go-review.googlesource.com/c/go/+/397678
Reviewed-by: Michael Pratt <mpratt@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>

src/runtime/export_test.go
src/runtime/malloc.go
src/runtime/mcache.go
src/runtime/mem.go
src/runtime/mgc.go
src/runtime/mgcpacer.go
src/runtime/mgcscavenge.go
src/runtime/mgcsweep.go
src/runtime/mheap.go
src/runtime/mstats.go

index 4025ac37433bb16c8fb1cdf8a3e804dd508ff579..0e64b87317f66dc623e34c198f845db5827d13ae 100644 (file)
@@ -1046,7 +1046,7 @@ func FreePageAlloc(pp *PageAlloc) {
        // sysUsed adds to p.sysStat and memstats.mappedReady no matter what
        // (and in anger should actually be accounted for), and there's no other
        // way to figure out how much we actually mapped.
-       memstats.mappedReady.Add(-int64(p.summaryMappedReady))
+       gcController.mappedReady.Add(-int64(p.summaryMappedReady))
        testSysStat.add(-int64(p.summaryMappedReady))
 
        // Free the mapped space for chunks.
index 30a2a5f2895be5de15d73434cbe6f1398b461e3d..f65be2bc749af73c967702df21608b39b5e18a7c 100644 (file)
@@ -566,7 +566,7 @@ func (h *mheap) sysAlloc(n uintptr) (v unsafe.Pointer, size uintptr) {
 
        // First, try the arena pre-reservation.
        // Newly-used mappings are considered released.
-       v = h.arena.alloc(n, heapArenaBytes, &memstats.heapReleased)
+       v = h.arena.alloc(n, heapArenaBytes, &gcController.heapReleased)
        if v != nil {
                size = n
                goto mapped
index 4e8ada5bda3cbae27996f39617a5776b5f1fe7bc..5a74431ff4c3eb5eae8f5682355328376dac50b7 100644 (file)
@@ -171,7 +171,7 @@ func (c *mcache) refill(spc spanClass) {
 
                // Count the allocs in inconsistent, internal stats.
                bytesAllocated := int64(slotsUsed * s.elemsize)
-               memstats.totalAlloc.Add(bytesAllocated)
+               gcController.totalAlloc.Add(bytesAllocated)
 
                // Update heapLive and flush scanAlloc.
                gcController.update(bytesAllocated, int64(c.scanAlloc))
@@ -229,7 +229,7 @@ func (c *mcache) allocLarge(size uintptr, noscan bool) *mspan {
        memstats.heapStats.release()
 
        // Count the alloc in inconsistent, internal stats.
-       memstats.totalAlloc.Add(int64(npages * pageSize))
+       gcController.totalAlloc.Add(int64(npages * pageSize))
 
        // Update heapLive.
        gcController.update(int64(s.npages*pageSize), 0)
@@ -260,7 +260,7 @@ func (c *mcache) releaseAll() {
 
                        // Adjust the actual allocs in inconsistent, internal stats.
                        // We assumed earlier that the full span gets allocated.
-                       memstats.totalAlloc.Add(int64(slotsUsed * s.elemsize))
+                       gcController.totalAlloc.Add(int64(slotsUsed * s.elemsize))
 
                        // Release the span to the mcentral.
                        mheap_.central[i].mcentral.uncacheSpan(s)
index f28e5367604f1d32e22b9612e63c1ced7fac4e92..2f43bdf788c701ac061c9296cbc6ef99a74d5a91 100644 (file)
@@ -47,7 +47,7 @@ import "unsafe"
 //go:nosplit
 func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer {
        sysStat.add(int64(n))
-       memstats.mappedReady.Add(int64(n))
+       gcController.mappedReady.Add(int64(n))
        return sysAllocOS(n)
 }
 
@@ -57,7 +57,7 @@ func sysAlloc(n uintptr, sysStat *sysMemStat) unsafe.Pointer {
 // sysUnused memory region are considered forfeit and the region must not be
 // accessed again until sysUsed is called.
 func sysUnused(v unsafe.Pointer, n uintptr) {
-       memstats.mappedReady.Add(-int64(n))
+       gcController.mappedReady.Add(-int64(n))
        sysUnusedOS(v, n)
 }
 
@@ -72,7 +72,7 @@ func sysUnused(v unsafe.Pointer, n uintptr) {
 // Prepared and Ready memory. However, the caller must provide the exact amout
 // of Prepared memory for accounting purposes.
 func sysUsed(v unsafe.Pointer, n, prepared uintptr) {
-       memstats.mappedReady.Add(int64(prepared))
+       gcController.mappedReady.Add(int64(prepared))
        sysUsedOS(v, n)
 }
 
@@ -97,7 +97,7 @@ func sysHugePage(v unsafe.Pointer, n uintptr) {
 //go:nosplit
 func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
        sysStat.add(-int64(n))
-       memstats.mappedReady.Add(-int64(n))
+       gcController.mappedReady.Add(-int64(n))
        sysFreeOS(v, n)
 }
 
@@ -111,7 +111,7 @@ func sysFree(v unsafe.Pointer, n uintptr, sysStat *sysMemStat) {
 // If a transition from Prepared is ever introduced, create a new function
 // that elides the Ready state accounting.
 func sysFault(v unsafe.Pointer, n uintptr) {
-       memstats.mappedReady.Add(-int64(n))
+       gcController.mappedReady.Add(-int64(n))
        sysFaultOS(v, n)
 }
 
index 5c821d8da5f4b5621088332fb56be90b40110a4e..e6663b01ac3b93014070f3eeec14cefe89a8daab 100644 (file)
@@ -983,7 +983,7 @@ func gcMarkTermination() {
        }
 
        // Record heapInUse for scavenger.
-       memstats.lastHeapInUse = memstats.heapInUse.load()
+       memstats.lastHeapInUse = gcController.heapInUse.load()
 
        // Update GC trigger and pacing for the next cycle.
        gcController.commit()
index 2824b73878b65654b5a7d4cb01ea5cae20b6f1ff..57c2215b4f57c60ae6e9ef80a08e088ca47b8cef 100644 (file)
@@ -355,6 +355,20 @@ type gcControllerState struct {
        // If this is zero, no fractional workers are needed.
        fractionalUtilizationGoal float64
 
+       // These memory stats are effectively duplicates of fields from
+       // memstats.heapStats but are updated atomically or with the world
+       // stopped and don't provide the same consistency guarantees.
+       //
+       // Because the runtime is responsible for managing a memory limit, it's
+       // useful to couple these stats more tightly to the gcController, which
+       // is intimately connected to how that memory limit is maintained.
+       heapInUse    sysMemStat    // bytes in mSpanInUse spans
+       heapReleased sysMemStat    // bytes released to the OS
+       heapFree     sysMemStat    // bytes not in any span, but not released to the OS
+       totalAlloc   atomic.Uint64 // total bytes allocated
+       totalFree    atomic.Uint64 // total bytes freed
+       mappedReady  atomic.Uint64 // total virtual memory in the Ready state (see mem.go).
+
        // test indicates that this is a test-only copy of gcControllerState.
        test bool
 
index 2cbb2cbfb6e3c27f799677fa67ae96671d0ac58a..4f44e0fa6128ca1482fc5a446717175093474806 100644 (file)
@@ -101,7 +101,7 @@ const (
 
 // heapRetained returns an estimate of the current heap RSS.
 func heapRetained() uint64 {
-       return memstats.heapInUse.load() + memstats.heapFree.load()
+       return gcController.heapInUse.load() + gcController.heapFree.load()
 }
 
 // gcPaceScavenger updates the scavenger's pacing, particularly
@@ -611,8 +611,8 @@ func printScavTrace(gen uint32, released uintptr, forced bool) {
        printlock()
        print("scav ", gen, " ",
                released>>10, " KiB work, ",
-               memstats.heapReleased.load()>>10, " KiB total, ",
-               (memstats.heapInUse.load()*100)/heapRetained(), "% util",
+               gcController.heapReleased.load()>>10, " KiB total, ",
+               (gcController.heapInUse.load()*100)/heapRetained(), "% util",
        )
        if forced {
                print(" (forced)")
@@ -913,8 +913,8 @@ func (p *pageAlloc) scavengeRangeLocked(ci chunkIdx, base, npages uint) uintptr
                // Update global accounting only when not in test, otherwise
                // the runtime's accounting will be wrong.
                nbytes := int64(npages) * pageSize
-               memstats.heapReleased.add(nbytes)
-               memstats.heapFree.add(-nbytes)
+               gcController.heapReleased.add(nbytes)
+               gcController.heapFree.add(-nbytes)
 
                // Update consistent accounting too.
                stats := memstats.heapStats.acquire()
index 365e21e35e24a0e6ee15eb23da477c6e9d0e3693..0a53cd451b4ac31a74106a71eaf6f68a170d8bae 100644 (file)
@@ -668,7 +668,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
                        memstats.heapStats.release()
 
                        // Count the frees in the inconsistent, internal stats.
-                       memstats.totalFree.Add(int64(nfreed) * int64(s.elemsize))
+                       gcController.totalFree.Add(int64(nfreed) * int64(s.elemsize))
                }
                if !preserve {
                        // The caller may not have removed this span from whatever
@@ -721,7 +721,7 @@ func (sl *sweepLocked) sweep(preserve bool) bool {
                        memstats.heapStats.release()
 
                        // Count the free in the inconsistent, internal stats.
-                       memstats.totalFree.Add(int64(size))
+                       gcController.totalFree.Add(int64(size))
 
                        return true
                }
index fcdd24c16e04e6d7a0ab4f058da8b72e896e4257..a54d268b3554749bab1ddf20f82c8ad8181cabdb 100644 (file)
@@ -1279,12 +1279,12 @@ HaveSpan:
                // sysUsed all the pages that are actually available
                // in the span since some of them might be scavenged.
                sysUsed(unsafe.Pointer(base), nbytes, scav)
-               memstats.heapReleased.add(-int64(scav))
+               gcController.heapReleased.add(-int64(scav))
        }
        // Update stats.
-       memstats.heapFree.add(-int64(nbytes - scav))
+       gcController.heapFree.add(-int64(nbytes - scav))
        if typ == spanAllocHeap {
-               memstats.heapInUse.add(int64(nbytes))
+               gcController.heapInUse.add(int64(nbytes))
        }
        // Update consistent stats.
        stats := memstats.heapStats.acquire()
@@ -1356,7 +1356,7 @@ func (h *mheap) grow(npage uintptr) (uintptr, bool) {
                // current arena, so we have to request the full ask.
                av, asize := h.sysAlloc(ask)
                if av == nil {
-                       inUse := memstats.heapFree.load() + memstats.heapReleased.load() + memstats.heapInUse.load()
+                       inUse := gcController.heapFree.load() + gcController.heapReleased.load() + gcController.heapInUse.load()
                        print("runtime: out of memory: cannot allocate ", ask, "-byte block (", inUse, " in use)\n")
                        return 0, false
                }
@@ -1373,7 +1373,7 @@ func (h *mheap) grow(npage uintptr) (uintptr, bool) {
                                // Transition this space from Reserved to Prepared and mark it
                                // as released since we'll be able to start using it after updating
                                // the page allocator and releasing the lock at any time.
-                               sysMap(unsafe.Pointer(h.curArena.base), size, &memstats.heapReleased)
+                               sysMap(unsafe.Pointer(h.curArena.base), size, &gcController.heapReleased)
                                // Update stats.
                                stats := memstats.heapStats.acquire()
                                atomic.Xaddint64(&stats.released, int64(size))
@@ -1404,7 +1404,7 @@ func (h *mheap) grow(npage uintptr) (uintptr, bool) {
        // The allocation is always aligned to the heap arena
        // size which is always > physPageSize, so its safe to
        // just add directly to heapReleased.
-       sysMap(unsafe.Pointer(v), nBase-v, &memstats.heapReleased)
+       sysMap(unsafe.Pointer(v), nBase-v, &gcController.heapReleased)
 
        // The memory just allocated counts as both released
        // and idle, even though it's not yet backed by spans.
@@ -1484,9 +1484,9 @@ func (h *mheap) freeSpanLocked(s *mspan, typ spanAllocType) {
        //
        // Mirrors the code in allocSpan.
        nbytes := s.npages * pageSize
-       memstats.heapFree.add(int64(nbytes))
+       gcController.heapFree.add(int64(nbytes))
        if typ == spanAllocHeap {
-               memstats.heapInUse.add(-int64(nbytes))
+               gcController.heapInUse.add(-int64(nbytes))
        }
        // Update consistent stats.
        stats := memstats.heapStats.acquire()
index 07abe240748d7fbb14365e4699230f936ba7bc2f..90e5b9590953fafc5f8c942b7e92a3621a18ee4c 100644 (file)
@@ -12,34 +12,10 @@ import (
        "unsafe"
 )
 
-// Statistics.
-//
-// For detailed descriptions see the documentation for MemStats.
-// Fields that differ from MemStats are further documented here.
-//
-// Many of these fields are updated on the fly, while others are only
-// updated when updatememstats is called.
 type mstats struct {
-       // Total virtual memory in the Ready state (see mem.go).
-       mappedReady atomic.Uint64
-
        // Statistics about malloc heap.
-
        heapStats consistentHeapStats
 
-       // These stats are effectively duplicates of fields from heapStats
-       // but are updated atomically or with the world stopped and don't
-       // provide the same consistency guarantees. They are used internally
-       // by the runtime.
-       //
-       // Like MemStats, heapInUse does not count memory in manually-managed
-       // spans.
-       heapInUse    sysMemStat    // bytes in mSpanInUse spans
-       heapReleased sysMemStat    // bytes released to the OS
-       heapFree     sysMemStat    // bytes not in any span, but not released to the OS
-       totalAlloc   atomic.Uint64 // total bytes allocated
-       totalFree    atomic.Uint64 // total bytes freed
-
        // Statistics about stacks.
        stacks_sys sysMemStat // only counts newosproc0 stack in mstats; differs from MemStats.StackSys
 
@@ -454,7 +430,7 @@ func readmemstats_m(stats *MemStats) {
        gcWorkBufInUse := uint64(consStats.inWorkBufs)
        gcProgPtrScalarBitsInUse := uint64(consStats.inPtrScalarBits)
 
-       totalMapped := memstats.heapInUse.load() + memstats.heapFree.load() + memstats.heapReleased.load() +
+       totalMapped := gcController.heapInUse.load() + gcController.heapFree.load() + gcController.heapReleased.load() +
                memstats.stacks_sys.load() + memstats.mspan_sys.load() + memstats.mcache_sys.load() +
                memstats.buckhash_sys.load() + memstats.gcMiscSys.load() + memstats.other_sys.load() +
                stackInUse + gcWorkBufInUse + gcProgPtrScalarBitsInUse
@@ -473,38 +449,38 @@ func readmemstats_m(stats *MemStats) {
        // TODO(mknyszek): Maybe don't throw here. It would be bad if a
        // bug in otherwise benign accounting caused the whole application
        // to crash.
-       if memstats.heapInUse.load() != uint64(consStats.inHeap) {
-               print("runtime: heapInUse=", memstats.heapInUse.load(), "\n")
+       if gcController.heapInUse.load() != uint64(consStats.inHeap) {
+               print("runtime: heapInUse=", gcController.heapInUse.load(), "\n")
                print("runtime: consistent value=", consStats.inHeap, "\n")
                throw("heapInUse and consistent stats are not equal")
        }
-       if memstats.heapReleased.load() != uint64(consStats.released) {
-               print("runtime: heapReleased=", memstats.heapReleased.load(), "\n")
+       if gcController.heapReleased.load() != uint64(consStats.released) {
+               print("runtime: heapReleased=", gcController.heapReleased.load(), "\n")
                print("runtime: consistent value=", consStats.released, "\n")
                throw("heapReleased and consistent stats are not equal")
        }
-       heapRetained := memstats.heapInUse.load() + memstats.heapFree.load()
+       heapRetained := gcController.heapInUse.load() + gcController.heapFree.load()
        consRetained := uint64(consStats.committed - consStats.inStacks - consStats.inWorkBufs - consStats.inPtrScalarBits)
        if heapRetained != consRetained {
                print("runtime: global value=", heapRetained, "\n")
                print("runtime: consistent value=", consRetained, "\n")
                throw("measures of the retained heap are not equal")
        }
-       if memstats.totalAlloc.Load() != totalAlloc {
-               print("runtime: totalAlloc=", memstats.totalAlloc.Load(), "\n")
+       if gcController.totalAlloc.Load() != totalAlloc {
+               print("runtime: totalAlloc=", gcController.totalAlloc.Load(), "\n")
                print("runtime: consistent value=", totalAlloc, "\n")
                throw("totalAlloc and consistent stats are not equal")
        }
-       if memstats.totalFree.Load() != totalFree {
-               print("runtime: totalFree=", memstats.totalFree.Load(), "\n")
+       if gcController.totalFree.Load() != totalFree {
+               print("runtime: totalFree=", gcController.totalFree.Load(), "\n")
                print("runtime: consistent value=", totalFree, "\n")
                throw("totalFree and consistent stats are not equal")
        }
        // Also check that mappedReady lines up with totalMapped - released.
        // This isn't really the same type of "make sure consistent stats line up" situation,
        // but this is an opportune time to check.
-       if memstats.mappedReady.Load() != totalMapped-uint64(consStats.released) {
-               print("runtime: mappedReady=", memstats.mappedReady.Load(), "\n")
+       if gcController.mappedReady.Load() != totalMapped-uint64(consStats.released) {
+               print("runtime: mappedReady=", gcController.mappedReady.Load(), "\n")
                print("runtime: totalMapped=", totalMapped, "\n")
                print("runtime: released=", uint64(consStats.released), "\n")
                print("runtime: totalMapped-released=", totalMapped-uint64(consStats.released), "\n")
@@ -519,7 +495,7 @@ func readmemstats_m(stats *MemStats) {
        stats.Mallocs = nMalloc
        stats.Frees = nFree
        stats.HeapAlloc = totalAlloc - totalFree
-       stats.HeapSys = memstats.heapInUse.load() + memstats.heapFree.load() + memstats.heapReleased.load()
+       stats.HeapSys = gcController.heapInUse.load() + gcController.heapFree.load() + gcController.heapReleased.load()
        // By definition, HeapIdle is memory that was mapped
        // for the heap but is not currently used to hold heap
        // objects. It also specifically is memory that can be
@@ -536,9 +512,9 @@ func readmemstats_m(stats *MemStats) {
        // HeapIdle = sys - stacks_inuse - gcWorkBufInUse - gcProgPtrScalarBitsInUse - heapInUse
        //
        // => HeapIdle = HeapSys - heapInUse = heapFree + heapReleased
-       stats.HeapIdle = memstats.heapFree.load() + memstats.heapReleased.load()
-       stats.HeapInuse = memstats.heapInUse.load()
-       stats.HeapReleased = memstats.heapReleased.load()
+       stats.HeapIdle = gcController.heapFree.load() + gcController.heapReleased.load()
+       stats.HeapInuse = gcController.heapInUse.load()
+       stats.HeapReleased = gcController.heapReleased.load()
        stats.HeapObjects = nMalloc - nFree
        stats.StackInuse = stackInUse
        // memstats.stacks_sys is only memory mapped directly for OS stacks.