diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 64cd4e77bf..628ddee896 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -41,9 +41,9 @@ func TestBinarySize(t *testing.T) { // This is a small number of very diverse targets that we want to test. tests := []sizeTest{ // microcontrollers - {"hifive1b", "examples/echo", 4484, 280, 0, 2252}, - {"microbit", "examples/serial", 2808, 388, 8, 2256}, - {"wioterminal", "examples/pininterrupt", 6064, 1484, 116, 6816}, + {"hifive1b", "examples/echo", 4560, 280, 0, 2268}, + {"microbit", "examples/serial", 2868, 388, 8, 2272}, + {"wioterminal", "examples/pininterrupt", 6104, 1484, 116, 6832}, // TODO: also check wasm. Right now this is difficult, because // wasm binaries are run through wasm-opt and therefore the diff --git a/src/runtime/gc_blocks.go b/src/runtime/gc_blocks.go index 7db6b7a1cf..1cc2384948 100644 --- a/src/runtime/gc_blocks.go +++ b/src/runtime/gc_blocks.go @@ -53,8 +53,10 @@ var ( 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 // total number of bytes allocated + gcTotalBlocks uint64 // total number of allocated blocks gcMallocs uint64 // total number of allocations gcFrees uint64 // total number of objects freed + gcFreedBlocks uint64 // total number of freed blocks ) // zeroSizedAlloc is just a sentinel that gets returned when allocating 0 bytes. @@ -285,6 +287,7 @@ func alloc(size uintptr, layout unsafe.Pointer) unsafe.Pointer { gcMallocs++ neededBlocks := (size + (bytesPerBlock - 1)) / bytesPerBlock + gcTotalBlocks += uint64(neededBlocks) // Continue looping until a run of free blocks has been found that fits the // requested size. @@ -619,6 +622,7 @@ func markRoot(addr, root uintptr) { // It returns how many bytes are free in the heap after the sweep. func sweep() (freeBytes uintptr) { freeCurrentObject := false + var freed uint64 for block := gcBlock(0); block < endBlock; block++ { switch block.state() { case blockStateHead: @@ -626,13 +630,13 @@ func sweep() (freeBytes uintptr) { block.markFree() freeCurrentObject = true gcFrees++ - freeBytes += bytesPerBlock + freed++ case blockStateTail: if freeCurrentObject { // This is a tail object following an unmarked head. // Free it now. block.markFree() - freeBytes += bytesPerBlock + freed++ } case blockStateMark: // This is a marked object. The next tail blocks must not be freed, @@ -644,6 +648,8 @@ func sweep() (freeBytes uintptr) { freeBytes += bytesPerBlock } } + gcFreedBlocks += freed + freeBytes += uintptr(freed) * bytesPerBlock return } @@ -690,6 +696,8 @@ func ReadMemStats(m *MemStats) { m.Mallocs = gcMallocs m.Frees = gcFrees m.Sys = uint64(heapEnd - heapStart) + m.HeapAlloc = (gcTotalBlocks - gcFreedBlocks) * uint64(bytesPerBlock) + m.Alloc = m.HeapAlloc } func SetFinalizer(obj interface{}, finalizer interface{}) { diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go index 2699e08b3f..7a6f8e637f 100644 --- a/src/runtime/mstats.go +++ b/src/runtime/mstats.go @@ -9,6 +9,11 @@ package runtime type MemStats struct { // General statistics. + // Alloc is bytes of allocated heap objects. + // + // This is the same as HeapAlloc (see below). + Alloc uint64 + // Sys is the total bytes of memory obtained from the OS. // // Sys is the sum of the XSys fields below. Sys measures the @@ -18,6 +23,19 @@ type MemStats struct { // Heap memory statistics. + // 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, total. // // In TinyGo unlike upstream Go, we make no distinction between