-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
runtime: fix finalization and profiling of tiny allocations
Handling of special records for tiny allocations has two problems: 1. Once we queue a finalizer we mark the object. As the result any subsequent finalizers for the same object will not be queued during this GC cycle. If we have 16 finalizers setup (the worst case), finalization will take 16 GC cycles. This is what caused misbehave of tinyfin.go. The actual flakiness was caused by the fact that fing is asynchronous and don't always run before the check. 2. If a tiny block has both finalizer and profile specials, it is possible that we both queue finalizer, preserve the object live and free the profile record. As the result heap profile can be skewed. Fix both issues by analyzing all special records for a single object at once. Also, make tinyfin test stricter and remove reliance on real time. Also, add a test for the problem 2. Currently heap profile missed about a half of live memory. Fixes golang#13100 Change-Id: I9ae4dc1c44893724138a4565ca5cae29f2e97544 Reviewed-on: https://go-review.googlesource.com/16591 Reviewed-by: Austin Clements <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Dmitry Vyukov <[email protected]>
- Loading branch information
Showing
4 changed files
with
142 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// run | ||
|
||
// Copyright 2015 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Test that tiny allocations with finalizers are correctly profiled. | ||
// Previously profile special records could have been processed prematurely | ||
// (while the object is still live). | ||
|
||
package main | ||
|
||
import ( | ||
"runtime" | ||
"time" | ||
"unsafe" | ||
) | ||
|
||
func main() { | ||
runtime.MemProfileRate = 1 | ||
// Allocate 1M 4-byte objects and set a finalizer for every third object. | ||
// Assuming that tiny block size is 16, some objects get finalizers setup | ||
// only for middle bytes. The finalizer resurrects that object. | ||
// As the result, all allocated memory must stay alive. | ||
const ( | ||
N = 1 << 20 | ||
tinyBlockSize = 16 // runtime._TinySize | ||
) | ||
hold := make([]*int32, 0, N) | ||
for i := 0; i < N; i++ { | ||
x := new(int32) | ||
if i%3 == 0 { | ||
runtime.SetFinalizer(x, func(p *int32) { | ||
hold = append(hold, p) | ||
}) | ||
} | ||
} | ||
// Finalize as much as possible. | ||
// Note: the sleep only increases probility of bug detection, | ||
// it cannot lead to false failure. | ||
for i := 0; i < 5; i++ { | ||
runtime.GC() | ||
time.Sleep(10 * time.Millisecond) | ||
} | ||
// Read memory profile. | ||
var prof []runtime.MemProfileRecord | ||
for { | ||
if n, ok := runtime.MemProfile(prof, false); ok { | ||
prof = prof[:n] | ||
break | ||
} else { | ||
prof = make([]runtime.MemProfileRecord, n+10) | ||
} | ||
} | ||
// See how much memory in tiny objects is profiled. | ||
var totalBytes int64 | ||
for _, p := range prof { | ||
bytes := p.AllocBytes - p.FreeBytes | ||
nobj := p.AllocObjects - p.FreeObjects | ||
size := bytes / nobj | ||
if size == tinyBlockSize { | ||
totalBytes += bytes | ||
} | ||
} | ||
// 2*tinyBlockSize slack is for any boundary effects. | ||
if want := N*int64(unsafe.Sizeof(int32(0))) - 2*tinyBlockSize; totalBytes < want { | ||
println("got", totalBytes, "want >=", want) | ||
panic("some of the tiny objects are not profiled") | ||
} | ||
// Just to keep hold alive. | ||
if len(hold) != 0 && hold[0] == nil { | ||
panic("bad") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters