github.com/aloncn/graphics-go@v0.0.1/src/runtime/mstats.go (about)

     1  // Copyright 2009 The Go Authors.  All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Memory statistics
     6  
     7  package runtime
     8  
     9  import (
    10  	"runtime/internal/atomic"
    11  	"runtime/internal/sys"
    12  	"unsafe"
    13  )
    14  
    15  // Statistics.
    16  // If you edit this structure, also edit type MemStats below.
    17  type mstats struct {
    18  	// General statistics.
    19  	alloc       uint64 // bytes allocated and not yet freed
    20  	total_alloc uint64 // bytes allocated (even if freed)
    21  	sys         uint64 // bytes obtained from system (should be sum of xxx_sys below, no locking, approximate)
    22  	nlookup     uint64 // number of pointer lookups
    23  	nmalloc     uint64 // number of mallocs
    24  	nfree       uint64 // number of frees
    25  
    26  	// Statistics about malloc heap.
    27  	// protected by mheap.lock
    28  	heap_alloc    uint64 // bytes allocated and not yet freed (same as alloc above)
    29  	heap_sys      uint64 // bytes obtained from system
    30  	heap_idle     uint64 // bytes in idle spans
    31  	heap_inuse    uint64 // bytes in non-idle spans
    32  	heap_released uint64 // bytes released to the os
    33  	heap_objects  uint64 // total number of allocated objects
    34  
    35  	// Statistics about allocation of low-level fixed-size structures.
    36  	// Protected by FixAlloc locks.
    37  	stacks_inuse uint64 // this number is included in heap_inuse above
    38  	stacks_sys   uint64 // always 0 in mstats
    39  	mspan_inuse  uint64 // mspan structures
    40  	mspan_sys    uint64
    41  	mcache_inuse uint64 // mcache structures
    42  	mcache_sys   uint64
    43  	buckhash_sys uint64 // profiling bucket hash table
    44  	gc_sys       uint64
    45  	other_sys    uint64
    46  
    47  	// Statistics about garbage collector.
    48  	// Protected by mheap or stopping the world during GC.
    49  	next_gc         uint64 // next gc (in heap_live time)
    50  	last_gc         uint64 // last gc (in absolute time)
    51  	pause_total_ns  uint64
    52  	pause_ns        [256]uint64 // circular buffer of recent gc pause lengths
    53  	pause_end       [256]uint64 // circular buffer of recent gc end times (nanoseconds since 1970)
    54  	numgc           uint32
    55  	gc_cpu_fraction float64 // fraction of CPU time used by GC
    56  	enablegc        bool
    57  	debuggc         bool
    58  
    59  	// Statistics about allocation size classes.
    60  
    61  	by_size [_NumSizeClasses]struct {
    62  		size    uint32
    63  		nmalloc uint64
    64  		nfree   uint64
    65  	}
    66  
    67  	// Statistics below here are not exported to Go directly.
    68  
    69  	tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly
    70  
    71  	// heap_live is the number of bytes considered live by the GC.
    72  	// That is: retained by the most recent GC plus allocated
    73  	// since then. heap_live <= heap_alloc, since heap_alloc
    74  	// includes unmarked objects that have not yet been swept (and
    75  	// hence goes up as we allocate and down as we sweep) while
    76  	// heap_live excludes these objects (and hence only goes up
    77  	// between GCs).
    78  	//
    79  	// This is updated atomically without locking. To reduce
    80  	// contention, this is updated only when obtaining a span from
    81  	// an mcentral and at this point it counts all of the
    82  	// unallocated slots in that span (which will be allocated
    83  	// before that mcache obtains another span from that
    84  	// mcentral). Hence, it slightly overestimates the "true" live
    85  	// heap size. It's better to overestimate than to
    86  	// underestimate because 1) this triggers the GC earlier than
    87  	// necessary rather than potentially too late and 2) this
    88  	// leads to a conservative GC rate rather than a GC rate that
    89  	// is potentially too low.
    90  	//
    91  	// Whenever this is updated, call traceHeapAlloc() and
    92  	// gcController.revise().
    93  	heap_live uint64
    94  
    95  	// heap_scan is the number of bytes of "scannable" heap. This
    96  	// is the live heap (as counted by heap_live), but omitting
    97  	// no-scan objects and no-scan tails of objects.
    98  	//
    99  	// Whenever this is updated, call gcController.revise().
   100  	heap_scan uint64
   101  
   102  	// heap_marked is the number of bytes marked by the previous
   103  	// GC. After mark termination, heap_live == heap_marked, but
   104  	// unlike heap_live, heap_marked does not change until the
   105  	// next mark termination.
   106  	heap_marked uint64
   107  
   108  	// heap_reachable is an estimate of the reachable heap bytes
   109  	// at the end of the previous GC.
   110  	heap_reachable uint64
   111  }
   112  
   113  var memstats mstats
   114  
   115  // A MemStats records statistics about the memory allocator.
   116  type MemStats struct {
   117  	// General statistics.
   118  	Alloc      uint64 // bytes allocated and not yet freed
   119  	TotalAlloc uint64 // bytes allocated (even if freed)
   120  	Sys        uint64 // bytes obtained from system (sum of XxxSys below)
   121  	Lookups    uint64 // number of pointer lookups
   122  	Mallocs    uint64 // number of mallocs
   123  	Frees      uint64 // number of frees
   124  
   125  	// Main allocation heap statistics.
   126  	HeapAlloc    uint64 // bytes allocated and not yet freed (same as Alloc above)
   127  	HeapSys      uint64 // bytes obtained from system
   128  	HeapIdle     uint64 // bytes in idle spans
   129  	HeapInuse    uint64 // bytes in non-idle span
   130  	HeapReleased uint64 // bytes released to the OS
   131  	HeapObjects  uint64 // total number of allocated objects
   132  
   133  	// Low-level fixed-size structure allocator statistics.
   134  	//	Inuse is bytes used now.
   135  	//	Sys is bytes obtained from system.
   136  	StackInuse  uint64 // bytes used by stack allocator
   137  	StackSys    uint64
   138  	MSpanInuse  uint64 // mspan structures
   139  	MSpanSys    uint64
   140  	MCacheInuse uint64 // mcache structures
   141  	MCacheSys   uint64
   142  	BuckHashSys uint64 // profiling bucket hash table
   143  	GCSys       uint64 // GC metadata
   144  	OtherSys    uint64 // other system allocations
   145  
   146  	// Garbage collector statistics.
   147  	NextGC        uint64 // next collection will happen when HeapAlloc ≥ this amount
   148  	LastGC        uint64 // end time of last collection (nanoseconds since 1970)
   149  	PauseTotalNs  uint64
   150  	PauseNs       [256]uint64 // circular buffer of recent GC pause durations, most recent at [(NumGC+255)%256]
   151  	PauseEnd      [256]uint64 // circular buffer of recent GC pause end times
   152  	NumGC         uint32
   153  	GCCPUFraction float64 // fraction of CPU time used by GC
   154  	EnableGC      bool
   155  	DebugGC       bool
   156  
   157  	// Per-size allocation statistics.
   158  	// 61 is NumSizeClasses in the C code.
   159  	BySize [61]struct {
   160  		Size    uint32
   161  		Mallocs uint64
   162  		Frees   uint64
   163  	}
   164  }
   165  
   166  // Size of the trailing by_size array differs between Go and C,
   167  // and all data after by_size is local to runtime, not exported.
   168  // NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
   169  // sizeof_C_MStats is what C thinks about size of Go struct.
   170  var sizeof_C_MStats = unsafe.Offsetof(memstats.by_size) + 61*unsafe.Sizeof(memstats.by_size[0])
   171  
   172  func init() {
   173  	var memStats MemStats
   174  	if sizeof_C_MStats != unsafe.Sizeof(memStats) {
   175  		println(sizeof_C_MStats, unsafe.Sizeof(memStats))
   176  		throw("MStats vs MemStatsType size mismatch")
   177  	}
   178  }
   179  
   180  // ReadMemStats populates m with memory allocator statistics.
   181  func ReadMemStats(m *MemStats) {
   182  	stopTheWorld("read mem stats")
   183  
   184  	systemstack(func() {
   185  		readmemstats_m(m)
   186  	})
   187  
   188  	startTheWorld()
   189  }
   190  
   191  func readmemstats_m(stats *MemStats) {
   192  	updatememstats(nil)
   193  
   194  	// Size of the trailing by_size array differs between Go and C,
   195  	// NumSizeClasses was changed, but we can not change Go struct because of backward compatibility.
   196  	memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats)
   197  
   198  	// Stack numbers are part of the heap numbers, separate those out for user consumption
   199  	stats.StackSys += stats.StackInuse
   200  	stats.HeapInuse -= stats.StackInuse
   201  	stats.HeapSys -= stats.StackInuse
   202  }
   203  
   204  //go:linkname readGCStats runtime/debug.readGCStats
   205  func readGCStats(pauses *[]uint64) {
   206  	systemstack(func() {
   207  		readGCStats_m(pauses)
   208  	})
   209  }
   210  
   211  func readGCStats_m(pauses *[]uint64) {
   212  	p := *pauses
   213  	// Calling code in runtime/debug should make the slice large enough.
   214  	if cap(p) < len(memstats.pause_ns)+3 {
   215  		throw("short slice passed to readGCStats")
   216  	}
   217  
   218  	// Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns.
   219  	lock(&mheap_.lock)
   220  
   221  	n := memstats.numgc
   222  	if n > uint32(len(memstats.pause_ns)) {
   223  		n = uint32(len(memstats.pause_ns))
   224  	}
   225  
   226  	// The pause buffer is circular. The most recent pause is at
   227  	// pause_ns[(numgc-1)%len(pause_ns)], and then backward
   228  	// from there to go back farther in time. We deliver the times
   229  	// most recent first (in p[0]).
   230  	p = p[:cap(p)]
   231  	for i := uint32(0); i < n; i++ {
   232  		j := (memstats.numgc - 1 - i) % uint32(len(memstats.pause_ns))
   233  		p[i] = memstats.pause_ns[j]
   234  		p[n+i] = memstats.pause_end[j]
   235  	}
   236  
   237  	p[n+n] = memstats.last_gc
   238  	p[n+n+1] = uint64(memstats.numgc)
   239  	p[n+n+2] = memstats.pause_total_ns
   240  	unlock(&mheap_.lock)
   241  	*pauses = p[:n+n+3]
   242  }
   243  
   244  //go:nowritebarrier
   245  func updatememstats(stats *gcstats) {
   246  	if stats != nil {
   247  		*stats = gcstats{}
   248  	}
   249  	for mp := allm; mp != nil; mp = mp.alllink {
   250  		if stats != nil {
   251  			src := (*[unsafe.Sizeof(gcstats{}) / 8]uint64)(unsafe.Pointer(&mp.gcstats))
   252  			dst := (*[unsafe.Sizeof(gcstats{}) / 8]uint64)(unsafe.Pointer(stats))
   253  			for i, v := range src {
   254  				dst[i] += v
   255  			}
   256  			mp.gcstats = gcstats{}
   257  		}
   258  	}
   259  
   260  	memstats.mcache_inuse = uint64(mheap_.cachealloc.inuse)
   261  	memstats.mspan_inuse = uint64(mheap_.spanalloc.inuse)
   262  	memstats.sys = memstats.heap_sys + memstats.stacks_sys + memstats.mspan_sys +
   263  		memstats.mcache_sys + memstats.buckhash_sys + memstats.gc_sys + memstats.other_sys
   264  
   265  	// Calculate memory allocator stats.
   266  	// During program execution we only count number of frees and amount of freed memory.
   267  	// Current number of alive object in the heap and amount of alive heap memory
   268  	// are calculated by scanning all spans.
   269  	// Total number of mallocs is calculated as number of frees plus number of alive objects.
   270  	// Similarly, total amount of allocated memory is calculated as amount of freed memory
   271  	// plus amount of alive heap memory.
   272  	memstats.alloc = 0
   273  	memstats.total_alloc = 0
   274  	memstats.nmalloc = 0
   275  	memstats.nfree = 0
   276  	for i := 0; i < len(memstats.by_size); i++ {
   277  		memstats.by_size[i].nmalloc = 0
   278  		memstats.by_size[i].nfree = 0
   279  	}
   280  
   281  	// Flush MCache's to MCentral.
   282  	systemstack(flushallmcaches)
   283  
   284  	// Aggregate local stats.
   285  	cachestats()
   286  
   287  	// Scan all spans and count number of alive objects.
   288  	lock(&mheap_.lock)
   289  	for i := uint32(0); i < mheap_.nspan; i++ {
   290  		s := h_allspans[i]
   291  		if s.state != mSpanInUse {
   292  			continue
   293  		}
   294  		if s.sizeclass == 0 {
   295  			memstats.nmalloc++
   296  			memstats.alloc += uint64(s.elemsize)
   297  		} else {
   298  			memstats.nmalloc += uint64(s.ref)
   299  			memstats.by_size[s.sizeclass].nmalloc += uint64(s.ref)
   300  			memstats.alloc += uint64(s.ref) * uint64(s.elemsize)
   301  		}
   302  	}
   303  	unlock(&mheap_.lock)
   304  
   305  	// Aggregate by size class.
   306  	smallfree := uint64(0)
   307  	memstats.nfree = mheap_.nlargefree
   308  	for i := 0; i < len(memstats.by_size); i++ {
   309  		memstats.nfree += mheap_.nsmallfree[i]
   310  		memstats.by_size[i].nfree = mheap_.nsmallfree[i]
   311  		memstats.by_size[i].nmalloc += mheap_.nsmallfree[i]
   312  		smallfree += uint64(mheap_.nsmallfree[i]) * uint64(class_to_size[i])
   313  	}
   314  	memstats.nfree += memstats.tinyallocs
   315  	memstats.nmalloc += memstats.nfree
   316  
   317  	// Calculate derived stats.
   318  	memstats.total_alloc = uint64(memstats.alloc) + uint64(mheap_.largefree) + smallfree
   319  	memstats.heap_alloc = memstats.alloc
   320  	memstats.heap_objects = memstats.nmalloc - memstats.nfree
   321  }
   322  
   323  //go:nowritebarrier
   324  func cachestats() {
   325  	for i := 0; ; i++ {
   326  		p := allp[i]
   327  		if p == nil {
   328  			break
   329  		}
   330  		c := p.mcache
   331  		if c == nil {
   332  			continue
   333  		}
   334  		purgecachedstats(c)
   335  	}
   336  }
   337  
   338  //go:nowritebarrier
   339  func flushallmcaches() {
   340  	for i := 0; ; i++ {
   341  		p := allp[i]
   342  		if p == nil {
   343  			break
   344  		}
   345  		c := p.mcache
   346  		if c == nil {
   347  			continue
   348  		}
   349  		c.releaseAll()
   350  		stackcache_clear(c)
   351  	}
   352  }
   353  
   354  //go:nosplit
   355  func purgecachedstats(c *mcache) {
   356  	// Protected by either heap or GC lock.
   357  	h := &mheap_
   358  	memstats.heap_scan += uint64(c.local_scan)
   359  	c.local_scan = 0
   360  	memstats.tinyallocs += uint64(c.local_tinyallocs)
   361  	c.local_tinyallocs = 0
   362  	memstats.nlookup += uint64(c.local_nlookup)
   363  	c.local_nlookup = 0
   364  	h.largefree += uint64(c.local_largefree)
   365  	c.local_largefree = 0
   366  	h.nlargefree += uint64(c.local_nlargefree)
   367  	c.local_nlargefree = 0
   368  	for i := 0; i < len(c.local_nsmallfree); i++ {
   369  		h.nsmallfree[i] += uint64(c.local_nsmallfree[i])
   370  		c.local_nsmallfree[i] = 0
   371  	}
   372  }
   373  
   374  // Atomically increases a given *system* memory stat.  We are counting on this
   375  // stat never overflowing a uintptr, so this function must only be used for
   376  // system memory stats.
   377  //
   378  // The current implementation for little endian architectures is based on
   379  // xadduintptr(), which is less than ideal: xadd64() should really be used.
   380  // Using xadduintptr() is a stop-gap solution until arm supports xadd64() that
   381  // doesn't use locks.  (Locks are a problem as they require a valid G, which
   382  // restricts their useability.)
   383  //
   384  // A side-effect of using xadduintptr() is that we need to check for
   385  // overflow errors.
   386  //go:nosplit
   387  func mSysStatInc(sysStat *uint64, n uintptr) {
   388  	if sys.BigEndian != 0 {
   389  		atomic.Xadd64(sysStat, int64(n))
   390  		return
   391  	}
   392  	if val := atomic.Xadduintptr((*uintptr)(unsafe.Pointer(sysStat)), n); val < n {
   393  		print("runtime: stat overflow: val ", val, ", n ", n, "\n")
   394  		exit(2)
   395  	}
   396  }
   397  
   398  // Atomically decreases a given *system* memory stat.  Same comments as
   399  // mSysStatInc apply.
   400  //go:nosplit
   401  func mSysStatDec(sysStat *uint64, n uintptr) {
   402  	if sys.BigEndian != 0 {
   403  		atomic.Xadd64(sysStat, -int64(n))
   404  		return
   405  	}
   406  	if val := atomic.Xadduintptr((*uintptr)(unsafe.Pointer(sysStat)), uintptr(-int64(n))); val+n < n {
   407  		print("runtime: stat underflow: val ", val, ", n ", n, "\n")
   408  		exit(2)
   409  	}
   410  }