diff --git a/src/runtime/gc_conservative.go b/src/runtime/gc_conservative.go index bea46c9c..396e1a68 100644 --- a/src/runtime/gc_conservative.go +++ b/src/runtime/gc_conservative.go @@ -55,7 +55,9 @@ var ( metadataStart unsafe.Pointer // pointer to the start of the heap metadata nextAlloc gcBlock // the next block that should be tried by the allocator endBlock gcBlock // the block just past the end of the available space - gcTotalAlloc uint64 // for runtime.MemStats + gcTotalAlloc uint64 // total number of bytes allocated + gcMallocs uint64 // total number of allocations + gcFrees uint64 // total number of objects freed ) // zeroSizedAlloc is just a sentinel that gets returned when allocating 0 bytes. @@ -268,6 +270,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { } gcTotalAlloc += uint64(size) + gcMallocs++ neededBlocks := (size + (bytesPerBlock - 1)) / bytesPerBlock @@ -573,6 +576,7 @@ func sweep() { // Unmarked head. Free it, including all tail blocks following it. block.markFree() freeCurrentObject = true + gcFrees++ case blockStateTail: if freeCurrentObject { // This is a tail object following an unmarked head. diff --git a/src/runtime/gc_leaking.go b/src/runtime/gc_leaking.go index e0a9ddc8..eab67939 100644 --- a/src/runtime/gc_leaking.go +++ b/src/runtime/gc_leaking.go @@ -19,6 +19,12 @@ var heapptr = heapStart // Total amount allocated for runtime.MemStats var gcTotalAlloc uint64 +// Total number of calls to alloc() +var gcMallocs uint64 + +// Total number of objected freed; for leaking collector this stays 0 +const gcFrees = 0 + // Inlining alloc() speeds things up slightly but bloats the executable by 50%, // see https://github.com/tinygo-org/tinygo/issues/2674. So don't. // @@ -30,6 +36,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { size = align(size) addr := heapptr gcTotalAlloc += uint64(size) + gcMallocs++ heapptr += size for heapptr >= heapEnd { // Try to increase the heap and check again. diff --git a/src/runtime/gc_none.go b/src/runtime/gc_none.go index 789802ed..ab0b4b40 100644 --- a/src/runtime/gc_none.go +++ b/src/runtime/gc_none.go @@ -14,6 +14,8 @@ import ( const gcAsserts = false // perform sanity checks var gcTotalAlloc uint64 // for runtime.MemStats +var gcMallocs uint64 +var gcFrees uint64 func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 6a43c844..2699e08b 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -42,6 +42,13 @@ type MemStats struct { // objects are freed. TotalAlloc uint64 + // Mallocs is the cumulative count of heap objects allocated. + // The number of live objects is Mallocs - Frees. + Mallocs uint64 + + // Frees is the cumulative count of heap objects freed. + Frees uint64 + // Off-heap memory statistics. // // The following statistics measure runtime-internal diff --git a/src/runtime/mstats_conservative.go b/src/runtime/mstats_conservative.go index c7d12a07..1f2bb879 100644 --- a/src/runtime/mstats_conservative.go +++ b/src/runtime/mstats_conservative.go @@ -22,5 +22,7 @@ func ReadMemStats(m *MemStats) { m.HeapSys = m.HeapInuse + m.HeapIdle m.GCSys = uint64(heapEnd - uintptr(metadataStart)) m.TotalAlloc = gcTotalAlloc + m.Mallocs = gcMallocs + m.Frees = gcFrees m.Sys = uint64(heapEnd - heapStart) } diff --git a/src/runtime/mstats_leaking.go b/src/runtime/mstats_leaking.go index b7834386..f1cfa793 100644 --- a/src/runtime/mstats_leaking.go +++ b/src/runtime/mstats_leaking.go @@ -15,5 +15,7 @@ func ReadMemStats(m *MemStats) { m.HeapSys = m.HeapInuse + m.HeapIdle m.GCSys = 0 m.TotalAlloc = gcTotalAlloc + m.Mallocs = gcMallocs + m.Frees = gcFrees m.Sys = uint64(heapEnd - heapStart) }