-rob
Consider the program:
package main
import (
"fmt"
"runtime"
"time"
"github.com/petar/GoGauge/pprof"
)
func getSomeMem() []byte { return make([]byte, 1e7) } // allocate 10MB
func main() {
pprof.StartLogging("pprof.log", 1e8) // This dumps PPROF is a
separate file every 1/10 sec
time.Sleep(5e9)
e := 0
for k := 0; k < 10000; k++ {
_ = getSomeMem()
if k % 10 == 0 {
fmt.Printf("Read: %d/%d \r", k, e)
}
}
runtime.GC()
time.Sleep(60e9)
}
This program has three stages.
In stage 1, it waits for 5 seconds (and does nothing else).
The pprof for this stage looks like this:
Total: 1.0 MB (this varies between 0.5 MB to 1.5MB)
1.0 100.0% 100.0% 1.0 100.0% __cgo_allocate
0.0 0.0% 100.0% 0.5 50.0% __cgo_panic
Stage 2 is the loop. Here pprof looks like this:
Total: 39.2 MB
39.2 100.0% 100.0% 39.2 100.0% __cgo_allocate
0.0 0.0% 100.0% 1.0 2.6% __cgo_panic
Stage 3 is after garbage collection has been run, and the program is
sleeping for 1 minute:
Total: 16.5 MB
16.5 100.0% 100.0% 16.5 100.0% __cgo_allocate
0.0 0.0% 100.0% 1.0 6.0% __cgo_panic
Why are there 16MB of allocated memory still?
If I run "top" on this program, the real size of this program is about 51MB
and it never drops (even after the loop ends and garbage collection happens).
Here's something even more confusing:
If I change the make([]byte, ...) allocation size from 10MB to
something higher or lower,
*all* numbers in the above dumps (as well as the "top" numbers) scale
accordingly.
This makes it look like: If I make a large slice and then delete it, I
will end up having
less real memory than if I made a small slice and then deleted it.
What is the explanation?
Thanks,
--Petar
What is the explanation?
OK, I have a related question regarding PPROF dumps and their meaning.
I've obtained a PPROF dump of a web server which has been running
for a few days (and served thousands of requests), however at the
time the dump was taken (nothing was being served) and the memory utilization
should have been close to 0.
All the server does is serve static files by reading them from disk,
serving them and then forgetting their contents. So, if serving no requests,
you'd expect next to no memory in use.
The real memory used by the process is 200M (at quiet time). Let's assume
that this is because Go does not return memory to the OS and
perhaps 200M was utilized at some peak time.
The PPROF during quiet time is pasted below. My question is:
Does the PPROF dump account just for things that are currently
legitimately allocated blocks (GC thinks of them as in-use) or does
it account for all blocks allocated throughout the life of the process?
I ask because:
If PPROF only shows current in-use blocks, then the dump below would
imply that I have a memory leak. This is very unlikely because the code
is so simple that I have manually verified multiple times that it has no leaks.
On the other hand, if PPROF shows stats for blocks allocated over past
times as well, then this dump would be consistent with the fact that
Go does not return memory to the OS.
Total: 201.5 MB
201.5 100.0% 100.0% 201.5 100.0% runtime.mallocgc
0.0 0.0% 100.0% 0.5 0.2% allocsg
0.0 0.0% 100.0% 4.0 2.0% crosscall_amd64
0.0 0.0% 100.0% 1.5 0.7%
github%2ecom/petar/GoHTTP/http.*ServerConn·Read
0.0 0.0% 100.0% 188.0 93.3%
github%2ecom/petar/GoHTTP/http.NewBodyFile
0.0 0.0% 100.0% 188.0 93.3%
github%2ecom/petar/GoHTTP/http.NewResponseFile
0.0 0.0% 100.0% 0.5 0.2%
github%2ecom/petar/GoHTTP/http.ReadRequest
0.0 0.0% 100.0% 1.0 0.5% github%2ecom/petar/GoHTTP/http._func_001
0.0 0.0% 100.0% 2.5 1.2%
github%2ecom/petar/GoHTTP/server.*Query·ContinueAndWrite
0.0 0.0% 100.0% 2.5 1.2%
github%2ecom/petar/GoHTTP/server.*Query·Write
0.0 0.0% 100.0% 193.5 96.0%
github%2ecom/petar/GoHTTP/server.*Server·Read
0.0 0.0% 100.0% 2.5 1.2%
github%2ecom/petar/GoHTTP/server.*Server·extRevIter
0.0 0.0% 100.0% 193.5 96.0%
github%2ecom/petar/GoHTTP/server.*Server·process
0.0 0.0% 100.0% 3.5 1.7%
github%2ecom/petar/GoHTTP/server.*Server·read
0.0 0.0% 100.0% 3.0 1.5%
github%2ecom/petar/GoHTTP/server.*Server·subIter
0.0 0.0% 100.0% 1.5 0.7%
github%2ecom/petar/GoHTTP/server.*StampedServerConn·Read
0.0 0.0% 100.0% 190.0 94.3%
github%2ecom/petar/GoHTTP/server/subs.*StaticSub·Serve
0.0 0.0% 100.0% 188.0 93.3% io/ioutil.ReadFile
0.0 0.0% 100.0% 187.5 93.1% io/ioutil.readAll
Thanks,
--Petar
Thanks,
--Petar
GoHTTP/http.*ServerConn·Read
GoHTTP/http.NewBodyFile
GoHTTP/server.*Query·Write
GoHTTP/server.*Server·extRevIter
GoHTTP/server.*StampedServerConn·Read
GoHTTP/server/subs.*StaticSub·Serve
io/ioutil.ReadFile
Usually instead of running top10 to get the output
you showed, it is more interesting to run web and
look at the graph, which makes the relevant stack
traces easier to extract.
Russ