Ala*_*air 5 garbage-collection memory-leaks go
我非常非常小心记忆,因为我必须编写需要处理大量数据集的程序。
\n\n目前我的应用程序很快就达到了 32GB 内存,开始交换,然后被系统杀死。
\n\n我不明白这是怎么回事,因为除了结构体之外,所有变量都是可收集的(在函数中并快速TokensStruct释放TokensCount)Trainer。TokensCount只是一个单位。TokensStruct是 [5]uint32 和字符串的 1,000,000 行切片,因此这意味着 20 个字节 + 字符串,我们可以称之为每条记录最多 50 个字节。50*1000000 = 需要 50MB 内存。因此,该脚本不应在函数中使用超过 50MB + 开销 + 临时可收集变量(最多可能还有 50MB)。 的最大潜在大小TokensStruct是 5,000,000,因为这是 的大小dictionary,但即使如此,它也只有 250MB的记忆。dictionary是一张地图,显然使用了大约 600MB 的内存,因为这就是应用程序的启动方式,但这不是问题,因为dictionary只加载一次,再也不会写入。
相反,它使用 32GB 内存然后就死掉了。从它执行此操作的速度来看,如果可以的话,我预计它会很乐意达到 1TB 内存。内存似乎随着加载文件的大小以线性方式增加,这意味着它似乎永远不会清除任何内存。进入应用程序的所有内容都会分配更多内存,并且内存永远不会被释放。
\n\n我尝试实施runtime.GC()以防垃圾收集运行得不够频繁,但这没有什么区别。
由于内存使用量以线性方式增加,因此这意味着GetTokens()或中存在内存泄漏LoadZip()。我不知道这是怎么回事,因为它们都是函数,只执行一项任务然后关闭。或者可能是tokensin 中的变量Start()是泄漏的原因。基本上,看起来加载和解析的每个文件都永远不会从内存中释放,因为这是内存以线性方式填满并继续上升到 32GB++ 的唯一方法。
绝对的噩梦!Go 有什么问题吗?有任何解决这个问题的方法吗?
\n\npackage main\n\nimport (\n "bytes"\n "code.google.com/p/go.text/transform"\n "code.google.com/p/go.text/unicode/norm"\n "compress/zlib"\n "encoding/gob"\n "fmt"\n "github.com/AlasdairF/BinSearch"\n "io/ioutil"\n "os"\n "regexp"\n "runtime"\n "strings"\n "unicode"\n "unicode/utf8"\n)\n\ntype TokensStruct struct {\n binsearch.Key_string\n Value [][5]uint32\n}\n\ntype Trainer struct {\n Tokens TokensStruct\n TokensCount uint\n}\n\nfunc checkErr(err error) {\n if err == nil {\n return\n }\n fmt.Println(`Some Error:`, err)\n panic(err)\n}\n\n// Local helper function for normalization of UTF8 strings.\nfunc isMn(r rune) bool {\n return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks\n}\n\n// This map is used by RemoveAccents function to convert non-accented characters.\nvar transliterations = map[rune]string{\'\xc3\x86\': "E", \'\xc3\x90\': "D", \'\xc5\x81\': "L", \'\xc3\x98\': "OE", \'\xc3\x9e\': "Th", \'\xc3\x9f\': "ss", \'\xc3\xa6\': "e", \'\xc3\xb0\': "d", \'\xc5\x82\': "l", \'\xc3\xb8\': "oe", \'\xc3\xbe\': "th", \'\xc5\x92\': "OE", \'\xc5\x93\': "oe"}\n\n// removeAccentsBytes converts accented UTF8 characters into their non-accented equivalents, from a []byte.\nfunc removeAccentsBytesDashes(b []byte) ([]byte, error) {\n mnBuf := make([]byte, len(b))\n t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)\n n, _, err := t.Transform(mnBuf, b, true)\n if err != nil {\n return nil, err\n }\n mnBuf = mnBuf[:n]\n tlBuf := bytes.NewBuffer(make([]byte, 0, len(mnBuf)*2))\n for i, w := 0, 0; i < len(mnBuf); i += w {\n r, width := utf8.DecodeRune(mnBuf[i:])\n if r == \'-\' {\n tlBuf.WriteByte(\' \')\n } else {\n if d, ok := transliterations[r]; ok {\n tlBuf.WriteString(d)\n } else {\n tlBuf.WriteRune(r)\n }\n }\n w = width\n }\n return tlBuf.Bytes(), nil\n}\n\nfunc LoadZip(filename string) ([]byte, error) {\n // Open file for reading\n fi, err := os.Open(filename)\n if err != nil {\n return nil, err\n }\n defer fi.Close()\n // Attach ZIP reader\n fz, err := zlib.NewReader(fi)\n if err != nil {\n return nil, err\n }\n defer fz.Close()\n // Pull\n data, err := ioutil.ReadAll(fz)\n if err != nil {\n return nil, err\n }\n return norm.NFC.Bytes(data), nil // return normalized\n}\n\nfunc getTokens(pibn string) []string {\n var data []byte\n var err error\n data, err = LoadZip(`/storedir/` + pibn + `/text.zip`)\n checkErr(err)\n data, err = removeAccentsBytesDashes(data)\n checkErr(err)\n data = bytes.ToLower(data)\n data = reg2.ReplaceAll(data, []byte("$2")) // remove contractions\n data = reg.ReplaceAllLiteral(data, nil)\n tokens := strings.Fields(string(data))\n return tokens\n}\n\nfunc (t *Trainer) Start() {\n data, err := ioutil.ReadFile(`list.txt`)\n checkErr(err)\n pibns := bytes.Fields(data)\n for i, pibn := range pibns {\n tokens := getTokens(string(pibn))\n t.addTokens(tokens)\n if i%100 == 0 {\n runtime.GC() // I added this just to try to stop the memory craziness, but it makes no difference\n }\n }\n}\n\nfunc (t *Trainer) addTokens(tokens []string) {\n for _, tok := range tokens {\n if _, ok := dictionary[tok]; ok {\n if indx, ok2 := t.Tokens.Find(tok); ok2 {\n ar := t.Tokens.Value[indx]\n ar[0]++\n t.Tokens.Value[indx] = ar\n t.TokensCount++\n } else {\n t.Tokens.AddKeyAt(tok, indx)\n t.Tokens.Value = append(t.Tokens.Value, [5]uint32{0, 0, 0, 0, 0})\n copy(t.Tokens.Value[indx+1:], t.Tokens.Value[indx:])\n t.Tokens.Value[indx] = [5]uint32{1, 0, 0, 0, 0}\n t.TokensCount++\n }\n }\n }\n return\n}\n\nfunc LoadDictionary() {\n dictionary = make(map[string]bool)\n data, err := ioutil.ReadFile(`dictionary`)\n checkErr(err)\n words := bytes.Fields(data)\n for _, word := range words {\n strword := string(word)\n dictionary[strword] = false\n }\n}\n\nvar reg = regexp.MustCompile(`[^a-z0-9\\s]`)\nvar reg2 = regexp.MustCompile(`\\b(c|l|all|dall|dell|nell|sull|coll|pell|gl|agl|dagl|degl|negl|sugl|un|m|t|s|v|d|qu|n|j)\'([a-z])`) //contractions\nvar dictionary map[string]bool\n\nfunc main() {\n trainer := new(Trainer)\n LoadDictionary()\n trainer.Start()\n}\nRun Code Online (Sandbox Code Playgroud)\n
1 “list.txt”和“字典”有多大?如果这么大,难怪内存这么大
pibns := bytes.Fields(data)
Run Code Online (Sandbox Code Playgroud)
多少钱len(pibns)?
2 启动 gc 调试( do GODEBUG="gctrace=1" ./yourprogram )以查看是否有任何 gc 发生
3 做一些像这样的配置文件:
func lookupMem(){
if f, err := os.Create("mem_prof"+time.Now.Unix()); err != nil {
log.Debug("record memory profile failed: %v", err)
} else {
runtime.GC()
pprof.WriteHeapProfile(f)
f.Close()
}
if f, err := os.Create("heap_prof" + "." + timestamp); err != nil {
log.Debug("heap profile failed:", err)
} else {
p := pprof.Lookup("heap")
p.WriteTo(f, 2)
}
}
func (t *Trainer) Start() {
.......
if i%1000==0 {
//if `len(pibns)` is not very large , record some meminfo
lookupMem()
}
.......
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4422 次 |
| 最近记录: |