我有一个使用太多内存并因此被杀的go程序,所以我想尝试保持内存使用率下降。这是我正在做的简化愚蠢版本,揭示了这个问题:
package main
import (
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"runtime"
"runtime/debug"
"strconv"
"time"
)
func main() {
source := "/tmp/1G.source"
repeats, _ := strconv.Atoi(os.Args[1])
m := &runtime.MemStats{}
err := exec.Command("dd", "if=/dev/zero", "of="+source, "bs=1073741824", "count=1").Run()
if err != nil {
log.Fatalf("failed to create 1GB file: %s\n", err)
}
fmt.Printf("created 1GB source file, %s\n", memory_usage(m))
// read it multiple times
switch os.Args[2] {
case "1":
fmt.Println("re-using a byte slice and emptying it each time")
// var data []byte
for i := 1; i <= repeats; i++ {
data, _ := ioutil.ReadFile(source)
if len(data) > 0 { // just so we use data
data = nil
}
fmt.Printf("did read %d, %s\n", i, memory_usage(m))
}
case "2":
fmt.Println("ignoring the return value entirely")
for i := 1; i <= repeats; i++ {
ioutil.ReadFile(source)
fmt.Printf("did read %d, %s\n", i, memory_usage(m))
}
case "3":
fmt.Println("ignoring the return value entirely, forcing memory freeing")
for i := 1; i <= repeats; i++ {
ioutil.ReadFile(source)
debug.FreeOSMemory()
fmt.Printf("did read %d, %s\n", i, memory_usage(m))
}
}
// wait incase garbage collection needs time to do something
<-time.After(5 * time.Second)
fmt.Printf("all done, %s\n", memory_usage(m))
os.Exit(0)
}
func memory_usage(m *runtime.MemStats) string {
runtime.ReadMemStats(m)
return fmt.Sprintf("system memory: %dMB; heap alloc: %dMB; heap idle-released: %dMB", int((m.Sys/1024)/1024), int((m.HeapAlloc/1024)/1024), int(((m.HeapIdle-m.HeapReleased)/1024)/1024))
}
如果我用main 7 2
打电话给我:
created 1GB source file, system memory: 2MB; heap alloc: 0MB; heap idle-released: 1MB
ignoring the return value entirely
did read 1, system memory: 4233MB; heap alloc: 3072MB; heap idle-released: 1024MB
did read 2, system memory: 4233MB; heap alloc: 3072MB; heap idle-released: 1024MB
did read 3, system memory: 4233MB; heap alloc: 3072MB; heap idle-released: 1024MB
did read 4, system memory: 4233MB; heap alloc: 3072MB; heap idle-released: 1023MB
did read 5, system memory: 6347MB; heap alloc: 3584MB; heap idle-released: 2559MB
did read 6, system memory: 6347MB; heap alloc: 3072MB; heap idle-released: 3071MB
did read 7, system memory: 6347MB; heap alloc: 3072MB; heap idle-released: 3071MB
all done, system memory: 6347MB; heap alloc: 3072MB; heap idle-released: 3071MB
也许偏离主题,但是预计读取1GB文件会导致4GB的系统内存使用量?
无论如何,理想情况下我希望无限数量的相同循环使用〜恒定的内存量,而不是从4GB增加到6GB。
所以我认为强制释放内存会有所帮助,但main 7 3
给出了:
created 1GB source file, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
ignoring the return value entirely, forcing memory freeing
did read 1, system memory: 4237MB; heap alloc: 0MB; heap idle-released: 0MB
did read 2, system memory: 4237MB; heap alloc: 0MB; heap idle-released: 0MB
did read 3, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
did read 4, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
did read 5, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
did read 6, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
did read 7, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
all done, system memory: 6351MB; heap alloc: 0MB; heap idle-released: 0MB
如何保持所有循环的内存使用率下降?
根据评论中的建议,我尝试了一个新案例:
case "4":
fmt.Println("doing a streaming read")
b := make([]byte, 10000, 10000)
for i := 1; i <= repeats; i++ {
f, _ := os.Open(source)
r := bufio.NewReader(f)
for {
_, err := r.Read(b)
if err != nil {
break
}
}
fmt.Printf("did read %d, %s\n", i, memory_usage(m))
}
}
但是我仍然会通过循环次数来增加内存使用量:
created 1GB source file, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
doing a streaming read
did read 1, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
did read 2, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
did read 3, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
did read 4, system memory: 1MB; heap alloc: 0MB; heap idle-released: 0MB
did read 5, system memory: 2MB; heap alloc: 0MB; heap idle-released: 0MB
did read 6, system memory: 2MB; heap alloc: 0MB; heap idle-released: 0MB
did read 7, system memory: 2MB; heap alloc: 0MB; heap idle-released: 0MB
all done, system memory: 2MB; heap alloc: 0MB; heap idle-released: 0MB
概括一下这个问题,当你在循环中使用第三方功能(即,你无法控制他们如何在自己内部使用内存)时,并且每个人都做同样的事情。在循环中的时间,是否有任何方法可以强制Go重新使用已经分配的内存而不是从操作系统请求更多内存?