【发布时间】:2014-08-07 05:53:44
【问题描述】:
我非常非常注意记忆,因为我必须编写需要处理大量数据集的程序。
目前我的应用程序很快达到 32GB 内存,开始交换,然后被系统杀死。
我不明白这是怎么回事,因为除了Trainer 结构中的TokensStruct 和TokensCount 之外,所有变量都是可收集的(在函数中并快速释放)。 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 的内存。内存似乎随着正在加载的文件的大小呈线性增长,这意味着它似乎根本不会清除任何内存。进入应用程序的所有内容都被分配了更多内存,并且永远不会释放内存。
我尝试实现runtime.GC(),以防垃圾收集运行不够频繁,但这没有任何区别。
由于内存使用量以线性方式增加,这意味着GetTokens() 或LoadZip() 中存在内存泄漏。我不知道这是怎么回事,因为它们都是函数并且只执行一项任务然后关闭。或者可能是Start() 中的tokens 变量是导致泄漏的原因。基本上,看起来每个加载和解析的文件都不会从内存中释放,因为这是内存以线性方式填满并继续上升到 32GB++ 的唯一方法。
绝对的噩梦!围棋有什么问题?有什么办法解决这个问题?
package main
import (
"bytes"
"code.google.com/p/go.text/transform"
"code.google.com/p/go.text/unicode/norm"
"compress/zlib"
"encoding/gob"
"fmt"
"github.com/AlasdairF/BinSearch"
"io/ioutil"
"os"
"regexp"
"runtime"
"strings"
"unicode"
"unicode/utf8"
)
type TokensStruct struct {
binsearch.Key_string
Value [][5]uint32
}
type Trainer struct {
Tokens TokensStruct
TokensCount uint
}
func checkErr(err error) {
if err == nil {
return
}
fmt.Println(`Some Error:`, err)
panic(err)
}
// Local helper function for normalization of UTF8 strings.
func isMn(r rune) bool {
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}
// This map is used by RemoveAccents function to convert non-accented characters.
var transliterations = map[rune]string{'Æ': "E", 'Ð': "D", 'Ł': "L", 'Ø': "OE", 'Þ': "Th", 'ß': "ss", 'æ': "e", 'ð': "d", 'ł': "l", 'ø': "oe", 'þ': "th", 'Œ': "OE", 'œ': "oe"}
// removeAccentsBytes converts accented UTF8 characters into their non-accented equivalents, from a []byte.
func removeAccentsBytesDashes(b []byte) ([]byte, error) {
mnBuf := make([]byte, len(b))
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
n, _, err := t.Transform(mnBuf, b, true)
if err != nil {
return nil, err
}
mnBuf = mnBuf[:n]
tlBuf := bytes.NewBuffer(make([]byte, 0, len(mnBuf)*2))
for i, w := 0, 0; i < len(mnBuf); i += w {
r, width := utf8.DecodeRune(mnBuf[i:])
if r == '-' {
tlBuf.WriteByte(' ')
} else {
if d, ok := transliterations[r]; ok {
tlBuf.WriteString(d)
} else {
tlBuf.WriteRune(r)
}
}
w = width
}
return tlBuf.Bytes(), nil
}
func LoadZip(filename string) ([]byte, error) {
// Open file for reading
fi, err := os.Open(filename)
if err != nil {
return nil, err
}
defer fi.Close()
// Attach ZIP reader
fz, err := zlib.NewReader(fi)
if err != nil {
return nil, err
}
defer fz.Close()
// Pull
data, err := ioutil.ReadAll(fz)
if err != nil {
return nil, err
}
return norm.NFC.Bytes(data), nil // return normalized
}
func getTokens(pibn string) []string {
var data []byte
var err error
data, err = LoadZip(`/storedir/` + pibn + `/text.zip`)
checkErr(err)
data, err = removeAccentsBytesDashes(data)
checkErr(err)
data = bytes.ToLower(data)
data = reg2.ReplaceAll(data, []byte("$2")) // remove contractions
data = reg.ReplaceAllLiteral(data, nil)
tokens := strings.Fields(string(data))
return tokens
}
func (t *Trainer) Start() {
data, err := ioutil.ReadFile(`list.txt`)
checkErr(err)
pibns := bytes.Fields(data)
for i, pibn := range pibns {
tokens := getTokens(string(pibn))
t.addTokens(tokens)
if i%100 == 0 {
runtime.GC() // I added this just to try to stop the memory craziness, but it makes no difference
}
}
}
func (t *Trainer) addTokens(tokens []string) {
for _, tok := range tokens {
if _, ok := dictionary[tok]; ok {
if indx, ok2 := t.Tokens.Find(tok); ok2 {
ar := t.Tokens.Value[indx]
ar[0]++
t.Tokens.Value[indx] = ar
t.TokensCount++
} else {
t.Tokens.AddKeyAt(tok, indx)
t.Tokens.Value = append(t.Tokens.Value, [5]uint32{0, 0, 0, 0, 0})
copy(t.Tokens.Value[indx+1:], t.Tokens.Value[indx:])
t.Tokens.Value[indx] = [5]uint32{1, 0, 0, 0, 0}
t.TokensCount++
}
}
}
return
}
func LoadDictionary() {
dictionary = make(map[string]bool)
data, err := ioutil.ReadFile(`dictionary`)
checkErr(err)
words := bytes.Fields(data)
for _, word := range words {
strword := string(word)
dictionary[strword] = false
}
}
var reg = regexp.MustCompile(`[^a-z0-9\s]`)
var 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
var dictionary map[string]bool
func main() {
trainer := new(Trainer)
LoadDictionary()
trainer.Start()
}
【问题讨论】:
-
你分析过它吗? dave.cheney.net/2013/07/07/…
-
我正在对其进行分析,并将更新。
-
好的,因此使用您链接到的漂亮包对其进行分析会导致以下语句成为内存泄漏的罪魁祸首:
tokens := strings.Fields(string(data))。我已将其分成两行并再次运行以查看它是string()还是strings.Fields。 -
就是这个
string(data),特别是在这个里面是CALL runtime.slicebytetostring(SB)正在泄漏。 -
在我做的另一个测试中没有泄漏,所以必须先说明我对字节所做的事情。
标签: memory-leaks go garbage-collection