-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
memory leak in crypto/merkle (?) #3313
Comments
Best I can tell this is a memory leak in
with
The leak is also there if we use |
Hello @ebuchman, please try this simplified reproducer which just uses runtime.MemStats, replacing package main
import (
"crypto/sha256"
"runtime"
)
var sink string
func main() {
var before, after uint64
sink = "foo"
runtime.MemProfileRate = 1
ms := new(runtime.MemStats)
runtime.GC()
runtime.ReadMemStats(ms)
before = ms.Alloc
println("Before: ", before)
myFunc()
runtime.GC()
runtime.ReadMemStats(ms)
after = ms.Alloc
println("After: ", after)
println("Difference: ", after-before)
}
func myFunc() {
sha256.New()
if sink != "foo" {
panic("Just here to ensure code isn't optimized away")
}
} and running for ((i=0; i < 10; i++)) do echo -e "Run #$i\r" && go run ts.go && echo -e "\n" ;done On my computer, it gives Run #0
Before: 102168
After: 102168
Difference: 0
Run #1
Before: 102168
After: 104056
Difference: 1888
Run #2
Before: 100280
After: 102168
Difference: 1888
Run #3
Before: 102168
After: 102168
Difference: 0
Run #4
Before: 102168
After: 102168
Difference: 0
Run #5
Before: 100280
After: 100280
Difference: 0
Run #6
Before: 102168
After: 102168
Difference: 0
Run #7
Before: 102264
After: 102264
Difference: 0
Run #8
Before: 100280
After: 100280
Difference: 0
Run #9
Before: 102168
After: 102168
Difference: 0 |
For a deeper analysis, we might want to instead perform an experiment by turning off GC and seeing how many bytes are allocated by sha256.New and then afterwards trying to correlate those values with those that pprof shows. How did you come to this conclusion about sha256? Did you extract this code from a server? |
@ebuchman in your repro in there is a little tricky thing that might explain your problem Notice that you've got func main() {
runtime.MemProfileRate = 1
fmt.Println("vim-go")
list := [][]byte{
[]byte("hello"),
[]byte("goodbye"),
[]byte("ok"),
}
myFunc(list)
runtime.GC()
http.ListenAndServe("localhost:7070", nil)
fmt.Println(list) <---- Notice this?
} http.ListenAndServe blocks till we hit Ctrl-C, but you are retaining a reference here so |
Exactly, I slightly changed the example to the following: func main() {
runtime.MemProfileRate = 1
go func() {
http.ListenAndServe("localhost:7070", nil)
}()
otherFunc()
// runtime.GC()
runtime.GC()
fmt.Println("ok ...")
//time.Sleep(20*time.Second)
//fmt.Println("ok2 ...")
time.Sleep(120*time.Second) // <- this is only so I have plenty of time to see what is going on
}
func otherFunc() {
size := 100000
list := make([][]byte, size)
for i:=0; i<size; i++ {
list[i] = []byte("hello"+string(i))
}
myFunc(list)
fmt.Println("done my func")
//time.Sleep(time.Second*10)
}
func myFunc(list [][]byte) {
merkle.SimpleHashFromByteSlices(list)
} using |
Wasn't able to reproduce this as Alloc is 0 after GC. https://gist.github.com/jessysaurusrex/8d73c3df00c0a813d7aef4cb66eafe3d#file-sha256demo-go |
I was trying to debug what seemed like a mem leak in tendermint and sanity checking my understanding of the memory profiler led me to discover that sha256.New() seems to persist in the heap profiler.
See the second comment which is even simpler and doesn't use the |
Bumping this from milestone as seemingly non-critical, but should probably still be investigated |
As a follow up to [#3530] I also tried to reproduce a memory leak in package main
import (
"crypto/sha256"
"net/http"
_ "net/http/pprof"
"runtime"
"time"
"github.com/tendermint/tendermint/crypto/merkle"
)
func main() {
go func() {
http.ListenAndServe("localhost:7070", nil)
}()
var before, after uint64
runtime.MemProfileRate = 1
ms := new(runtime.MemStats)
runtime.GC()
runtime.ReadMemStats(ms)
before = ms.Alloc
size := 100000
list := make([][]byte, size)
for i := 0; i < size; i++ {
list[i] = []byte("hello" + string(i))
}
println("Before: ", before)
hashRecursive(list)
runtime.GC()
runtime.ReadMemStats(ms)
after = ms.Alloc
println("After: ", after)
println("Difference: ", int64(after)-int64(before))
time.Sleep(120 * time.Second)
}
func myFunc() {
sha256.New()
}
func hashRecursive(list [][]byte) {
merkle.SimpleHashFromByteSlices(list)
}
func hashIterative(list [][]byte) {
merkle.SimpleHashFromByteSlicesIterative(list)
} |
i can try to explain why there was an impression that sha256 leaks. first 4 values in each heap profile record are take a look at the following example: (btw you need to ensure that function returns interface and it is used somehow, otherwise compiler will inline those calls down to package main
import (
"crypto/sha1"
"crypto/sha256"
"fmt"
"hash"
"net/http"
_ "net/http/pprof"
"runtime"
)
func main() {
runtime.MemProfileRate = 1
fmt.Println(s1().Size())
runtime.GC()
h := s2()
defer func() {
fmt.Println(h.Size())
}()
runtime.GC()
fmt.Println(http.ListenAndServe("localhost:7070", nil))
}
func s1() hash.Hash {
return sha1.New()
}
func s2() hash.Hash {
return sha256.New()
} both values will be visible in heap profile, but sha256 version is referenced in defer and won't be released in this program (note the difference in inuse values):
|
Has this problem been resolved? Is it related to this(golang/go#42347)? |
closing as many people were not able to reproduce, and we optimized the merkle hashing recently |
Running a default single node causes the memory to grow.
Inspecting the difference in heap dumps as the memory grows suggests there's a leak in the use of the Merkle tree.
Running a very simple test confirms this.
For instance this code:
Leaves many of these such entries in the heap profile:
The text was updated successfully, but these errors were encountered: