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 7package runtime 8 9import ( 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// Their layouts must match exactly. 18// 19// For detailed descriptions see the documentation for MemStats. 20// Fields that differ from MemStats are further documented here. 21// 22// Many of these fields are updated on the fly, while others are only 23// updated when updatememstats is called. 24type mstats struct { 25 // General statistics. 26 alloc uint64 // bytes allocated and not yet freed 27 total_alloc uint64 // bytes allocated (even if freed) 28 sys uint64 // bytes obtained from system (should be sum of xxx_sys below, no locking, approximate) 29 nlookup uint64 // number of pointer lookups (unused) 30 nmalloc uint64 // number of mallocs 31 nfree uint64 // number of frees 32 33 // Statistics about malloc heap. 34 // Updated atomically, or with the world stopped. 35 // 36 // Like MemStats, heap_sys and heap_inuse do not count memory 37 // in manually-managed spans. 38 heap_alloc uint64 // bytes allocated and not yet freed (same as alloc above) 39 heap_sys uint64 // virtual address space obtained from system for GC'd heap 40 heap_idle uint64 // bytes in idle spans 41 heap_inuse uint64 // bytes in mSpanInUse spans 42 heap_released uint64 // bytes released to the os 43 44 // heap_objects is not used by the runtime directly and instead 45 // computed on the fly by updatememstats. 46 heap_objects uint64 // total number of allocated objects 47 48 // Statistics about allocation of low-level fixed-size structures. 49 // Protected by FixAlloc locks. 50 stacks_inuse uint64 // bytes in manually-managed stack spans; updated atomically or during STW 51 stacks_sys uint64 // only counts newosproc0 stack in mstats; differs from MemStats.StackSys 52 mspan_inuse uint64 // mspan structures 53 mspan_sys uint64 54 mcache_inuse uint64 // mcache structures 55 mcache_sys uint64 56 buckhash_sys uint64 // profiling bucket hash table 57 gc_sys uint64 // updated atomically or during STW 58 other_sys uint64 // updated atomically or during STW 59 60 // Statistics about garbage collector. 61 // Protected by mheap or stopping the world during GC. 62 next_gc uint64 // goal heap_live for when next GC ends; ^0 if disabled 63 last_gc_unix uint64 // last gc (in unix time) 64 pause_total_ns uint64 65 pause_ns [256]uint64 // circular buffer of recent gc pause lengths 66 pause_end [256]uint64 // circular buffer of recent gc end times (nanoseconds since 1970) 67 numgc uint32 68 numforcedgc uint32 // number of user-forced GCs 69 gc_cpu_fraction float64 // fraction of CPU time used by GC 70 enablegc bool 71 debuggc bool 72 73 // Statistics about allocation size classes. 74 75 by_size [_NumSizeClasses]struct { 76 size uint32 77 nmalloc uint64 78 nfree uint64 79 } 80 81 // Statistics below here are not exported to MemStats directly. 82 83 last_gc_nanotime uint64 // last gc (monotonic time) 84 tinyallocs uint64 // number of tiny allocations that didn't cause actual allocation; not exported to go directly 85 last_next_gc uint64 // next_gc for the previous GC 86 last_heap_inuse uint64 // heap_inuse at mark termination of the previous GC 87 88 // triggerRatio is the heap growth ratio that triggers marking. 89 // 90 // E.g., if this is 0.6, then GC should start when the live 91 // heap has reached 1.6 times the heap size marked by the 92 // previous cycle. This should be ≤ GOGC/100 so the trigger 93 // heap size is less than the goal heap size. This is set 94 // during mark termination for the next cycle's trigger. 95 triggerRatio float64 96 97 // gc_trigger is the heap size that triggers marking. 98 // 99 // When heap_live ≥ gc_trigger, the mark phase will start. 100 // This is also the heap size by which proportional sweeping 101 // must be complete. 102 // 103 // This is computed from triggerRatio during mark termination 104 // for the next cycle's trigger. 105 gc_trigger uint64 106 107 // heap_live is the number of bytes considered live by the GC. 108 // That is: retained by the most recent GC plus allocated 109 // since then. heap_live <= heap_alloc, since heap_alloc 110 // includes unmarked objects that have not yet been swept (and 111 // hence goes up as we allocate and down as we sweep) while 112 // heap_live excludes these objects (and hence only goes up 113 // between GCs). 114 // 115 // This is updated atomically without locking. To reduce 116 // contention, this is updated only when obtaining a span from 117 // an mcentral and at this point it counts all of the 118 // unallocated slots in that span (which will be allocated 119 // before that mcache obtains another span from that 120 // mcentral). Hence, it slightly overestimates the "true" live 121 // heap size. It's better to overestimate than to 122 // underestimate because 1) this triggers the GC earlier than 123 // necessary rather than potentially too late and 2) this 124 // leads to a conservative GC rate rather than a GC rate that 125 // is potentially too low. 126 // 127 // Reads should likewise be atomic (or during STW). 128 // 129 // Whenever this is updated, call traceHeapAlloc() and 130 // gcController.revise(). 131 heap_live uint64 132 133 // heap_scan is the number of bytes of "scannable" heap. This 134 // is the live heap (as counted by heap_live), but omitting 135 // no-scan objects and no-scan tails of objects. 136 // 137 // Whenever this is updated, call gcController.revise(). 138 heap_scan uint64 139 140 // heap_marked is the number of bytes marked by the previous 141 // GC. After mark termination, heap_live == heap_marked, but 142 // unlike heap_live, heap_marked does not change until the 143 // next mark termination. 144 heap_marked uint64 145} 146 147var memstats mstats 148 149// A MemStats records statistics about the memory allocator. 150type MemStats struct { 151 // General statistics. 152 153 // Alloc is bytes of allocated heap objects. 154 // 155 // This is the same as HeapAlloc (see below). 156 Alloc uint64 157 158 // TotalAlloc is cumulative bytes allocated for heap objects. 159 // 160 // TotalAlloc increases as heap objects are allocated, but 161 // unlike Alloc and HeapAlloc, it does not decrease when 162 // objects are freed. 163 TotalAlloc uint64 164 165 // Sys is the total bytes of memory obtained from the OS. 166 // 167 // Sys is the sum of the XSys fields below. Sys measures the 168 // virtual address space reserved by the Go runtime for the 169 // heap, stacks, and other internal data structures. It's 170 // likely that not all of the virtual address space is backed 171 // by physical memory at any given moment, though in general 172 // it all was at some point. 173 Sys uint64 174 175 // Lookups is the number of pointer lookups performed by the 176 // runtime. 177 // 178 // This is primarily useful for debugging runtime internals. 179 Lookups uint64 180 181 // Mallocs is the cumulative count of heap objects allocated. 182 // The number of live objects is Mallocs - Frees. 183 Mallocs uint64 184 185 // Frees is the cumulative count of heap objects freed. 186 Frees uint64 187 188 // Heap memory statistics. 189 // 190 // Interpreting the heap statistics requires some knowledge of 191 // how Go organizes memory. Go divides the virtual address 192 // space of the heap into "spans", which are contiguous 193 // regions of memory 8K or larger. A span may be in one of 194 // three states: 195 // 196 // An "idle" span contains no objects or other data. The 197 // physical memory backing an idle span can be released back 198 // to the OS (but the virtual address space never is), or it 199 // can be converted into an "in use" or "stack" span. 200 // 201 // An "in use" span contains at least one heap object and may 202 // have free space available to allocate more heap objects. 203 // 204 // A "stack" span is used for goroutine stacks. Stack spans 205 // are not considered part of the heap. A span can change 206 // between heap and stack memory; it is never used for both 207 // simultaneously. 208 209 // HeapAlloc is bytes of allocated heap objects. 210 // 211 // "Allocated" heap objects include all reachable objects, as 212 // well as unreachable objects that the garbage collector has 213 // not yet freed. Specifically, HeapAlloc increases as heap 214 // objects are allocated and decreases as the heap is swept 215 // and unreachable objects are freed. Sweeping occurs 216 // incrementally between GC cycles, so these two processes 217 // occur simultaneously, and as a result HeapAlloc tends to 218 // change smoothly (in contrast with the sawtooth that is 219 // typical of stop-the-world garbage collectors). 220 HeapAlloc uint64 221 222 // HeapSys is bytes of heap memory obtained from the OS. 223 // 224 // HeapSys measures the amount of virtual address space 225 // reserved for the heap. This includes virtual address space 226 // that has been reserved but not yet used, which consumes no 227 // physical memory, but tends to be small, as well as virtual 228 // address space for which the physical memory has been 229 // returned to the OS after it became unused (see HeapReleased 230 // for a measure of the latter). 231 // 232 // HeapSys estimates the largest size the heap has had. 233 HeapSys uint64 234 235 // HeapIdle is bytes in idle (unused) spans. 236 // 237 // Idle spans have no objects in them. These spans could be 238 // (and may already have been) returned to the OS, or they can 239 // be reused for heap allocations, or they can be reused as 240 // stack memory. 241 // 242 // HeapIdle minus HeapReleased estimates the amount of memory 243 // that could be returned to the OS, but is being retained by 244 // the runtime so it can grow the heap without requesting more 245 // memory from the OS. If this difference is significantly 246 // larger than the heap size, it indicates there was a recent 247 // transient spike in live heap size. 248 HeapIdle uint64 249 250 // HeapInuse is bytes in in-use spans. 251 // 252 // In-use spans have at least one object in them. These spans 253 // can only be used for other objects of roughly the same 254 // size. 255 // 256 // HeapInuse minus HeapAlloc estimates the amount of memory 257 // that has been dedicated to particular size classes, but is 258 // not currently being used. This is an upper bound on 259 // fragmentation, but in general this memory can be reused 260 // efficiently. 261 HeapInuse uint64 262 263 // HeapReleased is bytes of physical memory returned to the OS. 264 // 265 // This counts heap memory from idle spans that was returned 266 // to the OS and has not yet been reacquired for the heap. 267 HeapReleased uint64 268 269 // HeapObjects is the number of allocated heap objects. 270 // 271 // Like HeapAlloc, this increases as objects are allocated and 272 // decreases as the heap is swept and unreachable objects are 273 // freed. 274 HeapObjects uint64 275 276 // Stack memory statistics. 277 // 278 // Stacks are not considered part of the heap, but the runtime 279 // can reuse a span of heap memory for stack memory, and 280 // vice-versa. 281 282 // StackInuse is bytes in stack spans. 283 // 284 // In-use stack spans have at least one stack in them. These 285 // spans can only be used for other stacks of the same size. 286 // 287 // There is no StackIdle because unused stack spans are 288 // returned to the heap (and hence counted toward HeapIdle). 289 StackInuse uint64 290 291 // StackSys is bytes of stack memory obtained from the OS. 292 // 293 // StackSys is StackInuse, plus any memory obtained directly 294 // from the OS for OS thread stacks (which should be minimal). 295 StackSys uint64 296 297 // Off-heap memory statistics. 298 // 299 // The following statistics measure runtime-internal 300 // structures that are not allocated from heap memory (usually 301 // because they are part of implementing the heap). Unlike 302 // heap or stack memory, any memory allocated to these 303 // structures is dedicated to these structures. 304 // 305 // These are primarily useful for debugging runtime memory 306 // overheads. 307 308 // MSpanInuse is bytes of allocated mspan structures. 309 MSpanInuse uint64 310 311 // MSpanSys is bytes of memory obtained from the OS for mspan 312 // structures. 313 MSpanSys uint64 314 315 // MCacheInuse is bytes of allocated mcache structures. 316 MCacheInuse uint64 317 318 // MCacheSys is bytes of memory obtained from the OS for 319 // mcache structures. 320 MCacheSys uint64 321 322 // BuckHashSys is bytes of memory in profiling bucket hash tables. 323 BuckHashSys uint64 324 325 // GCSys is bytes of memory in garbage collection metadata. 326 GCSys uint64 327 328 // OtherSys is bytes of memory in miscellaneous off-heap 329 // runtime allocations. 330 OtherSys uint64 331 332 // Garbage collector statistics. 333 334 // NextGC is the target heap size of the next GC cycle. 335 // 336 // The garbage collector's goal is to keep HeapAlloc ≤ NextGC. 337 // At the end of each GC cycle, the target for the next cycle 338 // is computed based on the amount of reachable data and the 339 // value of GOGC. 340 NextGC uint64 341 342 // LastGC is the time the last garbage collection finished, as 343 // nanoseconds since 1970 (the UNIX epoch). 344 LastGC uint64 345 346 // PauseTotalNs is the cumulative nanoseconds in GC 347 // stop-the-world pauses since the program started. 348 // 349 // During a stop-the-world pause, all goroutines are paused 350 // and only the garbage collector can run. 351 PauseTotalNs uint64 352 353 // PauseNs is a circular buffer of recent GC stop-the-world 354 // pause times in nanoseconds. 355 // 356 // The most recent pause is at PauseNs[(NumGC+255)%256]. In 357 // general, PauseNs[N%256] records the time paused in the most 358 // recent N%256th GC cycle. There may be multiple pauses per 359 // GC cycle; this is the sum of all pauses during a cycle. 360 PauseNs [256]uint64 361 362 // PauseEnd is a circular buffer of recent GC pause end times, 363 // as nanoseconds since 1970 (the UNIX epoch). 364 // 365 // This buffer is filled the same way as PauseNs. There may be 366 // multiple pauses per GC cycle; this records the end of the 367 // last pause in a cycle. 368 PauseEnd [256]uint64 369 370 // NumGC is the number of completed GC cycles. 371 NumGC uint32 372 373 // NumForcedGC is the number of GC cycles that were forced by 374 // the application calling the GC function. 375 NumForcedGC uint32 376 377 // GCCPUFraction is the fraction of this program's available 378 // CPU time used by the GC since the program started. 379 // 380 // GCCPUFraction is expressed as a number between 0 and 1, 381 // where 0 means GC has consumed none of this program's CPU. A 382 // program's available CPU time is defined as the integral of 383 // GOMAXPROCS since the program started. That is, if 384 // GOMAXPROCS is 2 and a program has been running for 10 385 // seconds, its "available CPU" is 20 seconds. GCCPUFraction 386 // does not include CPU time used for write barrier activity. 387 // 388 // This is the same as the fraction of CPU reported by 389 // GODEBUG=gctrace=1. 390 GCCPUFraction float64 391 392 // EnableGC indicates that GC is enabled. It is always true, 393 // even if GOGC=off. 394 EnableGC bool 395 396 // DebugGC is currently unused. 397 DebugGC bool 398 399 // BySize reports per-size class allocation statistics. 400 // 401 // BySize[N] gives statistics for allocations of size S where 402 // BySize[N-1].Size < S ≤ BySize[N].Size. 403 // 404 // This does not report allocations larger than BySize[60].Size. 405 BySize [61]struct { 406 // Size is the maximum byte size of an object in this 407 // size class. 408 Size uint32 409 410 // Mallocs is the cumulative count of heap objects 411 // allocated in this size class. The cumulative bytes 412 // of allocation is Size*Mallocs. The number of live 413 // objects in this size class is Mallocs - Frees. 414 Mallocs uint64 415 416 // Frees is the cumulative count of heap objects freed 417 // in this size class. 418 Frees uint64 419 } 420} 421 422// Size of the trailing by_size array differs between mstats and MemStats, 423// and all data after by_size is local to runtime, not exported. 424// NumSizeClasses was changed, but we cannot change MemStats because of backward compatibility. 425// sizeof_C_MStats is the size of the prefix of mstats that 426// corresponds to MemStats. It should match Sizeof(MemStats{}). 427var sizeof_C_MStats = unsafe.Offsetof(memstats.by_size) + 61*unsafe.Sizeof(memstats.by_size[0]) 428 429func init() { 430 var memStats MemStats 431 if sizeof_C_MStats != unsafe.Sizeof(memStats) { 432 println(sizeof_C_MStats, unsafe.Sizeof(memStats)) 433 throw("MStats vs MemStatsType size mismatch") 434 } 435 436 if unsafe.Offsetof(memstats.heap_live)%8 != 0 { 437 println(unsafe.Offsetof(memstats.heap_live)) 438 throw("memstats.heap_live not aligned to 8 bytes") 439 } 440} 441 442// ReadMemStats populates m with memory allocator statistics. 443// 444// The returned memory allocator statistics are up to date as of the 445// call to ReadMemStats. This is in contrast with a heap profile, 446// which is a snapshot as of the most recently completed garbage 447// collection cycle. 448func ReadMemStats(m *MemStats) { 449 stopTheWorld("read mem stats") 450 451 systemstack(func() { 452 readmemstats_m(m) 453 }) 454 455 startTheWorld() 456} 457 458func readmemstats_m(stats *MemStats) { 459 updatememstats() 460 461 // The size of the trailing by_size array differs between 462 // mstats and MemStats. NumSizeClasses was changed, but we 463 // cannot change MemStats because of backward compatibility. 464 memmove(unsafe.Pointer(stats), unsafe.Pointer(&memstats), sizeof_C_MStats) 465 466 // memstats.stacks_sys is only memory mapped directly for OS stacks. 467 // Add in heap-allocated stack memory for user consumption. 468 stats.StackSys += stats.StackInuse 469} 470 471//go:linkname readGCStats runtime..z2fdebug.readGCStats 472func readGCStats(pauses *[]uint64) { 473 systemstack(func() { 474 readGCStats_m(pauses) 475 }) 476} 477 478// readGCStats_m must be called on the system stack because it acquires the heap 479// lock. See mheap for details. 480//go:systemstack 481func readGCStats_m(pauses *[]uint64) { 482 p := *pauses 483 // Calling code in runtime/debug should make the slice large enough. 484 if cap(p) < len(memstats.pause_ns)+3 { 485 throw("short slice passed to readGCStats") 486 } 487 488 // Pass back: pauses, pause ends, last gc (absolute time), number of gc, total pause ns. 489 lock(&mheap_.lock) 490 491 n := memstats.numgc 492 if n > uint32(len(memstats.pause_ns)) { 493 n = uint32(len(memstats.pause_ns)) 494 } 495 496 // The pause buffer is circular. The most recent pause is at 497 // pause_ns[(numgc-1)%len(pause_ns)], and then backward 498 // from there to go back farther in time. We deliver the times 499 // most recent first (in p[0]). 500 p = p[:cap(p)] 501 for i := uint32(0); i < n; i++ { 502 j := (memstats.numgc - 1 - i) % uint32(len(memstats.pause_ns)) 503 p[i] = memstats.pause_ns[j] 504 p[n+i] = memstats.pause_end[j] 505 } 506 507 p[n+n] = memstats.last_gc_unix 508 p[n+n+1] = uint64(memstats.numgc) 509 p[n+n+2] = memstats.pause_total_ns 510 unlock(&mheap_.lock) 511 *pauses = p[:n+n+3] 512} 513 514//go:nowritebarrier 515func updatememstats() { 516 memstats.mcache_inuse = uint64(mheap_.cachealloc.inuse) 517 memstats.mspan_inuse = uint64(mheap_.spanalloc.inuse) 518 memstats.sys = memstats.heap_sys + memstats.stacks_sys + memstats.mspan_sys + 519 memstats.mcache_sys + memstats.buckhash_sys + memstats.gc_sys + memstats.other_sys 520 521 // We also count stacks_inuse as sys memory. 522 memstats.sys += memstats.stacks_inuse 523 524 // Calculate memory allocator stats. 525 // During program execution we only count number of frees and amount of freed memory. 526 // Current number of alive object in the heap and amount of alive heap memory 527 // are calculated by scanning all spans. 528 // Total number of mallocs is calculated as number of frees plus number of alive objects. 529 // Similarly, total amount of allocated memory is calculated as amount of freed memory 530 // plus amount of alive heap memory. 531 memstats.alloc = 0 532 memstats.total_alloc = 0 533 memstats.nmalloc = 0 534 memstats.nfree = 0 535 for i := 0; i < len(memstats.by_size); i++ { 536 memstats.by_size[i].nmalloc = 0 537 memstats.by_size[i].nfree = 0 538 } 539 540 // Flush mcache's to mcentral. 541 systemstack(flushallmcaches) 542 543 // Aggregate local stats. 544 cachestats() 545 546 // Collect allocation stats. This is safe and consistent 547 // because the world is stopped. 548 var smallFree, totalAlloc, totalFree uint64 549 // Collect per-spanclass stats. 550 for spc := range mheap_.central { 551 // The mcaches are now empty, so mcentral stats are 552 // up-to-date. 553 c := &mheap_.central[spc].mcentral 554 memstats.nmalloc += c.nmalloc 555 i := spanClass(spc).sizeclass() 556 memstats.by_size[i].nmalloc += c.nmalloc 557 totalAlloc += c.nmalloc * uint64(class_to_size[i]) 558 } 559 // Collect per-sizeclass stats. 560 for i := 0; i < _NumSizeClasses; i++ { 561 if i == 0 { 562 memstats.nmalloc += mheap_.nlargealloc 563 totalAlloc += mheap_.largealloc 564 totalFree += mheap_.largefree 565 memstats.nfree += mheap_.nlargefree 566 continue 567 } 568 569 // The mcache stats have been flushed to mheap_. 570 memstats.nfree += mheap_.nsmallfree[i] 571 memstats.by_size[i].nfree = mheap_.nsmallfree[i] 572 smallFree += mheap_.nsmallfree[i] * uint64(class_to_size[i]) 573 } 574 totalFree += smallFree 575 576 memstats.nfree += memstats.tinyallocs 577 memstats.nmalloc += memstats.tinyallocs 578 579 // Calculate derived stats. 580 memstats.total_alloc = totalAlloc 581 memstats.alloc = totalAlloc - totalFree 582 memstats.heap_alloc = memstats.alloc 583 memstats.heap_objects = memstats.nmalloc - memstats.nfree 584} 585 586// cachestats flushes all mcache stats. 587// 588// The world must be stopped. 589// 590//go:nowritebarrier 591func cachestats() { 592 for _, p := range allp { 593 c := p.mcache 594 if c == nil { 595 continue 596 } 597 purgecachedstats(c) 598 } 599} 600 601// flushmcache flushes the mcache of allp[i]. 602// 603// The world must be stopped. 604// 605//go:nowritebarrier 606func flushmcache(i int) { 607 p := allp[i] 608 c := p.mcache 609 if c == nil { 610 return 611 } 612 c.releaseAll() 613} 614 615// flushallmcaches flushes the mcaches of all Ps. 616// 617// The world must be stopped. 618// 619//go:nowritebarrier 620func flushallmcaches() { 621 for i := 0; i < int(gomaxprocs); i++ { 622 flushmcache(i) 623 } 624} 625 626//go:nosplit 627func purgecachedstats(c *mcache) { 628 // Protected by either heap or GC lock. 629 h := &mheap_ 630 memstats.heap_scan += uint64(c.local_scan) 631 c.local_scan = 0 632 memstats.tinyallocs += uint64(c.local_tinyallocs) 633 c.local_tinyallocs = 0 634 h.largefree += uint64(c.local_largefree) 635 c.local_largefree = 0 636 h.nlargefree += uint64(c.local_nlargefree) 637 c.local_nlargefree = 0 638 for i := 0; i < len(c.local_nsmallfree); i++ { 639 h.nsmallfree[i] += uint64(c.local_nsmallfree[i]) 640 c.local_nsmallfree[i] = 0 641 } 642} 643 644// Atomically increases a given *system* memory stat. We are counting on this 645// stat never overflowing a uintptr, so this function must only be used for 646// system memory stats. 647// 648// The current implementation for little endian architectures is based on 649// xadduintptr(), which is less than ideal: xadd64() should really be used. 650// Using xadduintptr() is a stop-gap solution until arm supports xadd64() that 651// doesn't use locks. (Locks are a problem as they require a valid G, which 652// restricts their useability.) 653// 654// A side-effect of using xadduintptr() is that we need to check for 655// overflow errors. 656//go:nosplit 657func mSysStatInc(sysStat *uint64, n uintptr) { 658 if sysStat == nil { 659 return 660 } 661 if sys.BigEndian { 662 atomic.Xadd64(sysStat, int64(n)) 663 return 664 } 665 if val := atomic.Xadduintptr((*uintptr)(unsafe.Pointer(sysStat)), n); val < n { 666 print("runtime: stat overflow: val ", val, ", n ", n, "\n") 667 exit(2) 668 } 669} 670 671// Atomically decreases a given *system* memory stat. Same comments as 672// mSysStatInc apply. 673//go:nosplit 674func mSysStatDec(sysStat *uint64, n uintptr) { 675 if sysStat == nil { 676 return 677 } 678 if sys.BigEndian { 679 atomic.Xadd64(sysStat, -int64(n)) 680 return 681 } 682 if val := atomic.Xadduintptr((*uintptr)(unsafe.Pointer(sysStat)), uintptr(-int64(n))); val+n < n { 683 print("runtime: stat underflow: val ", val, ", n ", n, "\n") 684 exit(2) 685 } 686} 687