From f67c9de65621a46905a2f923b35634f74fa8635b Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Fri, 9 Sep 2016 21:43:50 -0400 Subject: [PATCH] runtime: document MemStats This documents all fields in MemStats and more clearly documents where mstats differs from MemStats. Fixes #15849. Change-Id: Ie09374bcdb3a5fdd2d25fe4bba836aaae92cb1dd Reviewed-on: https://go-review.googlesource.com/28972 Reviewed-by: Rob Pike Reviewed-by: Hyang-Ah Hana Kim --- src/runtime/mstats.go | 337 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 288 insertions(+), 49 deletions(-) diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 2f3c58358c..69e7660cc6 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -14,6 +14,13 @@ import ( // Statistics. // If you edit this structure, also edit type MemStats below. +// Their layouts must match exactly. +// +// 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 { // General statistics. alloc uint64 // bytes allocated and not yet freed @@ -24,18 +31,36 @@ type mstats struct { nfree uint64 // number of frees // Statistics about malloc heap. - // protected by mheap.lock + // Protected by mheap.lock + // + // In mstats, heap_sys and heap_inuse includes stack memory, + // while in MemStats stack memory is separated out from the + // heap stats. heap_alloc uint64 // bytes allocated and not yet freed (same as alloc above) - heap_sys uint64 // bytes obtained from system + heap_sys uint64 // virtual address space obtained from system heap_idle uint64 // bytes in idle spans heap_inuse uint64 // bytes in non-idle spans heap_released uint64 // bytes released to the os heap_objects uint64 // total number of allocated objects + // TODO(austin): heap_released is both useless and inaccurate + // in its current form. It's useless because, from the user's + // and OS's perspectives, there's no difference between a page + // that has not yet been faulted in and a page that has been + // released back to the OS. We could fix this by considering + // newly mapped spans to be "released". It's inaccurate + // because when we split a large span for allocation, we + // "unrelease" all pages in the large span and not just the + // ones we split off for use. This is trickier to fix because + // we currently don't know which pages of a span we've + // released. We could fix it by separating "free" and + // "released" spans, but then we have to allocate from runs of + // free and released spans. + // Statistics about allocation of low-level fixed-size structures. // Protected by FixAlloc locks. - stacks_inuse uint64 // this number is included in heap_inuse above - stacks_sys uint64 // always 0 in mstats + stacks_inuse uint64 // this number is included in heap_inuse above; differs from MemStats.StackInuse + stacks_sys uint64 // only counts newosproc0 stack in mstats; differs from MemStats.StackSys mspan_inuse uint64 // mspan structures mspan_sys uint64 mcache_inuse uint64 // mcache structures @@ -64,7 +89,7 @@ type mstats struct { nfree uint64 } - // Statistics below here are not exported to Go directly. + // Statistics below here are not exported to MemStats directly. tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly @@ -118,47 +143,254 @@ var memstats mstats // A MemStats records statistics about the memory allocator. type MemStats struct { // General statistics. - Alloc uint64 // bytes allocated and not yet freed - TotalAlloc uint64 // bytes allocated (even if freed) - Sys uint64 // bytes obtained from system (sum of XxxSys below) - Lookups uint64 // number of pointer lookups - Mallocs uint64 // number of mallocs - Frees uint64 // number of frees - - // Main allocation heap statistics. - HeapAlloc uint64 // bytes allocated and not yet freed (same as Alloc above) - HeapSys uint64 // bytes obtained from system - HeapIdle uint64 // bytes in idle spans - HeapInuse uint64 // bytes in non-idle span - HeapReleased uint64 // bytes released to the OS - HeapObjects uint64 // total number of allocated objects - - // Low-level fixed-size structure allocator statistics. - // Inuse is bytes used now. - // Sys is bytes obtained from system. - StackInuse uint64 // bytes used by stack allocator - StackSys uint64 - MSpanInuse uint64 // mspan structures - MSpanSys uint64 - MCacheInuse uint64 // mcache structures - MCacheSys uint64 - BuckHashSys uint64 // profiling bucket hash table - GCSys uint64 // GC metadata - OtherSys uint64 // other system allocations + + // Alloc is bytes of allocated heap objects. + // + // This is the same as HeapAlloc (see below). + Alloc uint64 + + // TotalAlloc is cumulative bytes allocated for heap objects. + // + // TotalAlloc increases as heap objects are allocated, but + // unlike Alloc and HeapAlloc, it does not decrease when + // objects are freed. + TotalAlloc uint64 + + // Sys is the total bytes of memory obtained from the OS. + // + // Sys is the sum of the XSys fields below. Sys measures the + // virtual address space reserved by the Go runtime for the + // heap, stacks, and other internal data structures. It's + // likely that not all of the virtual address space is backed + // by physical memory at any given moment, though in general + // it all was at some point. + Sys uint64 + + // Lookups is the number of pointer lookups performed by the + // runtime. + // + // This is primarily useful for debugging runtime internals. + Lookups uint64 + + // Mallocs is the cumulative count of heap objects allocated. + Mallocs uint64 + + // Frees is the cumulative count of heap objects freed. + Frees uint64 + + // Heap memory statistics. + // + // Interpreting the heap statistics requires some knowledge of + // how Go organizes memory. Go divides the virtual address + // space of the heap into "spans", which are contiguous + // regions of memory 8K or larger. A span may be in one of + // three states: + // + // An "idle" span contains no objects or other data. The + // physical memory backing an idle span can be released back + // to the OS (but the virtual address space never is), or it + // can be converted into an "in use" or "stack" span. + // + // An "in use" span contains at least one heap object and may + // have free space available to allocate more heap objects. + // + // A "stack" span is used for goroutine stacks. Stack spans + // are not considered part of the heap. A span can change + // between heap and stack memory; it is never used for both + // simultaneously. + + // HeapAlloc is bytes of allocated heap objects. + // + // "Allocated" heap objects include all reachable objects, as + // well as unreachable objects that the garbage collector has + // not yet freed. Specifically, HeapAlloc increases as heap + // objects are allocated and decreases as the heap is swept + // and unreachable objects are freed. Sweeping occurs + // incrementally between GC cycles, so these two processes + // occur simultaneously, and as a result HeapAlloc tends to + // change smoothly (in contrast with the sawtooth that is + // typical of stop-the-world garbage collectors). + HeapAlloc uint64 + + // HeapSys is bytes of heap memory obtained from the OS. + // + // HeapSys measures the amount of virtual address space + // reserved for the heap. This includes virtual address space + // that has been reserved but not yet used, which consumes no + // physical memory, but tends to be small, as well as virtual + // address space for which the physical memory has been + // returned to the OS after it became unused (see HeapReleased + // for a measure of the latter). + // + // HeapSys estimates the largest size the heap has had. + HeapSys uint64 + + // HeapIdle is bytes in idle (unused) spans. + // + // Idle spans have no objects in them. These spans could be + // (and may already have been) returned to the OS, or they can + // be reused for heap allocations, or they can be reused as + // stack memory. + // + // HeapIdle minus HeapReleased estimates the amount of memory + // that could be returned to the OS, but is being retained by + // the runtime so it can grow the heap without requesting more + // memory from the OS. If this difference is significantly + // larger than the heap size, it indicates there was a recent + // transient spike in live heap size. + HeapIdle uint64 + + // HeapInuse is bytes in in-use spans. + // + // In-use spans have at least one object in them. These spans + // can only be used for other objects of roughly the same + // size. + // + // HeapInuse minus HeapAlloc esimates the amount of memory + // that has been dedicated to particular size classes, but is + // not currently being used. This is an upper bound on + // fragmentation, but in general this memory can be reused + // efficiently. + HeapInuse uint64 + + // HeapReleased is bytes of physical memory returned to the OS. + // + // This counts heap memory from idle spans that was returned + // to the OS and has not yet been reacquired for the heap. + HeapReleased uint64 + + // HeapObjects is the number of allocated heap objects. + // + // Like HeapAlloc, this increases as objects are allocated and + // decreases as the heap is swept and unreachable objects are + // freed. + HeapObjects uint64 + + // Stack memory statistics. + // + // Stacks are not considered part of the heap, but the runtime + // can reuse a span of heap memory for stack memory, and + // vice-versa. + + // StackInuse is bytes in stack spans. + // + // In-use stack spans have at least one stack in them. These + // spans can only be used for other stacks of the same size. + // + // There is no StackIdle because unused stack spans are + // returned to the heap (and hence counted toward HeapIdle). + StackInuse uint64 + + // StackSys is bytes of stack memory obtained from the OS. + // + // StackSys is StackInuse, plus any memory obtained directly + // from the OS for OS thread stacks (which should be minimal). + StackSys uint64 + + // Off-heap memory statistics. + // + // The following statistics measure runtime-internal + // structures that are not allocated from heap memory (usually + // because they are part of implementing the heap). Unlike + // heap or stack memory, any memory allocated to these + // structures is dedicated to these structures. + // + // These are primarily useful for debugging runtime memory + // overheads. + + // MSpanInuse is bytes of allocated mspan structures. + MSpanInuse uint64 + + // MSpanSys is bytes of memory obtained from the OS for mspan + // structures. + MSpanSys uint64 + + // MCacheInuse is bytes of allocated mcache structures. + MCacheInuse uint64 + + // MCacheSys is bytes of memory obtained from the OS for + // mcache structures. + MCacheSys uint64 + + // BuckHashSys is bytes of memory in profiling bucket hash tables. + BuckHashSys uint64 + + // GCSys is bytes of memory in garbage collection metadata. + GCSys uint64 + + // OtherSys is bytes of memory in miscellaneous off-heap + // runtime allocations. + OtherSys uint64 // Garbage collector statistics. - NextGC uint64 // next collection will happen when HeapAlloc ≥ this amount - LastGC uint64 // end time of last collection (nanoseconds since 1970) - PauseTotalNs uint64 - PauseNs [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256] - PauseEnd [256]uint64 // circular buffer of recent GC pause end times - NumGC uint32 - GCCPUFraction float64 // fraction of CPU time used by GC - EnableGC bool - DebugGC bool - - // Per-size allocation statistics. - // 61 is NumSizeClasses in the C code. + + // NextGC is the target heap size of the next GC cycle. + // + // The garbage collector's goal is to keep HeapAlloc ≤ NextGC. + // At the end of each GC cycle, the target for the next cycle + // is computed based on the amount of reachable data and the + // value of GOGC. + NextGC uint64 + + // LastGC is the time the last garbage collection finished, as + // nanoseconds since 1970 (the UNIX epoch). + LastGC uint64 + + // PauseTotalNs is the cumulative nanoseconds in GC + // stop-the-world pauses since the program started. + // + // During a stop-the-world pause, all goroutines are paused + // and only the garbage collector can run. + PauseTotalNs uint64 + + // PauseNs is a circular buffer of recent GC stop-the-world + // pause times in nanoseconds. + // + // The most recent pause is at PauseNs[(NumGC+255)%256]. In + // general, PauseNs[N%256] records the time paused in the most + // recent N%256th GC cycle. There may be multiple pauses per + // GC cycle; this is the sum of all pauses during a cycle. + PauseNs [256]uint64 + + // PauseEnd is a circular buffer of recent GC pause end times, + // as nanoseconds since 1970 (the UNIX epoch). + // + // This buffer is filled the same way as PauseNs. There may be + // multiple pauses per GC cycle; this records the end of the + // last pause in a cycle. + PauseEnd [256]uint64 + + // NumGC is the number of completed GC cycles. + NumGC uint32 + + // GCCPUFraction is the fraction of this program's available + // CPU time used by the GC since the program started. + // + // GCCPUFraction is expressed as a number between 0 and 1, + // where 0 means GC has consumed none of this program's CPU. A + // program's available CPU time is defined as the integral of + // GOMAXPROCS since the program started. That is, if + // GOMAXPROCS is 2 and a program has been running for 10 + // seconds, its "available CPU" is 20 seconds. GCCPUFraction + // does not include CPU time used for write barrier activity. + // + // This is the same as the fraction of CPU reported by + // GODEBUG=gctrace=1. + GCCPUFraction float64 + + // EnableGC indicates that GC is enabled. It is always true, + // even if GOGC=off. + EnableGC bool + + // DebugGC is currently unused. + DebugGC bool + + // BySize reports per-size class allocation statistics. + // + // BySize[N] gives statistics for allocations of size S where + // BySize[N-1].Size < S ≤ BySize[N].Size. + // + // This does not report allocations larger than BySize[60].Size. BySize [61]struct { Size uint32 Mallocs uint64 @@ -166,10 +398,11 @@ type MemStats struct { } } -// Size of the trailing by_size array differs between Go and C, +// Size of the trailing by_size array differs between mstats and MemStats, // and all data after by_size is local to runtime, not exported. -// NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility. -// sizeof_C_MStats is what C thinks about size of Go struct. +// NumSizeClasses was changed, but we cannot change MemStats because of backward compatibility. +// sizeof_C_MStats is the size of the prefix of mstats that +// corresponds to MemStats. It should match Sizeof(MemStats{}). var sizeof_C_MStats = unsafe.Offsetof(memstats.by_size) + 61*unsafe.Sizeof(memstats.by_size[0]) func init() { @@ -181,6 +414,11 @@ func init() { } // ReadMemStats populates m with memory allocator statistics. +// +// The returned memory allocator statistics are up to date as of the +// call to ReadMemStats. This is in contrast with a heap profile, +// which is a snapshot as of the most recently completed garbage +// collection cycle. func ReadMemStats(m *MemStats) { stopTheWorld("read mem stats") @@ -194,8 +432,9 @@ func ReadMemStats(m *MemStats) { func readmemstats_m(stats *MemStats) { updatememstats(nil) - // Size of the trailing by_size array differs between Go and C, - // NumSizeClasses was changed, but we cannot change Go struct because of backward compatibility. + // The size of the trailing by_size array differs between + // mstats and MemStats. NumSizeClasses was changed, but we + // cannot change MemStats because of backward compatibility. memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats) // Stack numbers are part of the heap numbers, separate those out for user consumption -- 2.48.1