Go:内存使用过多,内存泄漏

时间:2014-08-07 05:53:44

标签: memory-leaks go garbage-collection

我非常非常记忆,因为我必须编写需要处理大量数据集的程序。

目前我的应用程序很快就会达到32GB内存,开始交换,然后被系统杀死。

我不明白这是怎么回事,因为除了TokensStruct结构中的TokensCountTrainer之外,所有变量都是可收集的(在函数中并且快速发布)。 TokensCount只是一个小问题。 TokensStruct是[5] uint32和字符串的1,000,000行切片,因此这意味着20个字节+字符串,我们可以为每个记录调用最多50个字节。 50 * 1000000 =需要50MB内存。因此,这个脚本不应该在函数中使用超过50MB +开销+临时可收集变量(可能另外50MB最大)。TokensStruct的最大可能大小为5,000,000,因为这是{{1}的大小但即使这样,它只有250MB的内存。 dictionary是一张地图,显然使用了大约600MB的内存,因为这就是应用程序启动的方式,但这不是问题,因为dictionary只加载一次而且永远不会再写入。

相反,它使用32GB内存然后死掉。通过它的速度,我希望如果可能的话,它会愉快地获得1TB的内存。内存似乎以加载文件大小的线性方式增加,这意味着它似乎永远不会清除任何内存。进入应用程序的所有内容都分配了更多的内存,永远不会释放内存。

我尝试实施dictionary,以防垃圾收集活动不够频繁,但这没有任何区别。

由于内存使用量以线性方式增加,因此这意味着runtime.GC()GetTokens()中存在内存泄漏。我不知道这是怎么回事,因为它们都是功能,只做一个任务然后关闭。或者可能是LoadZip()中的tokens变量是泄漏的原因。基本上看起来每个加载和解析的文件都不会从内存中释放出来,因为这是内存以线性方式填充并保持上升到32GB ++的唯一方式。

绝对的噩梦! Go有什么问题?有什么方法可以解决这个问题吗?

Start()

2 个答案:

答案 0 :(得分:1)

如果您要从大字符串中进行标记,请确保避免内存固定。从上面的评论中,听起来像令牌是一个大字符串的子串。

您可能需要在getTokens()函数中添加一些额外内容,以确保令牌不会固定内存。

func getTokens(...) {
    // near the end of your program
    for i, t := range(tokens) {
        tokens[i] = string([]byte(t))
    }
}

顺便说一下,使用ioutil.ReadFile一次将整个文件读入内存看起来很可疑。您确定无法使用bufio.Scanner吗?

我正在密切关注代码......如果你真的关心内存,请利用io.Reader。您应该尽量避免一次性吸取整个文件的内容。使用io.Reader和transform“沿着谷物”。你现在使用它的方式是违背其意图的。您正在使用的转换包的重点是构建可以流式传输数据的灵活读者。

例如,这里简化了您正在做的事情:

package main

import (
    "bufio"
    "bytes"
    "fmt"
    "unicode/utf8"

    "code.google.com/p/go.text/transform"
)

type AccentsTransformer map[rune]string

func (a AccentsTransformer) Transform(dst, src []byte, atEOF bool) (nDst, nSrc int, err error) {
    for nSrc < len(src) {
        // If we're at the edge, note this and return.
        if !atEOF && !utf8.FullRune(src[nSrc:]) {
            err = transform.ErrShortSrc
            return
        }
        r, width := utf8.DecodeRune(src[nSrc:])
        if r == utf8.RuneError && width == 1 {
            err = fmt.Errorf("Decoding error")
            return
        }
        if d, ok := a[r]; ok {
            if nDst+len(d) > len(dst) {
                err = transform.ErrShortDst
                return
            }
            copy(dst[nDst:], d)
            nSrc += width
            nDst += len(d)
            continue
        }

        if nDst+width > len(dst) {
            err = transform.ErrShortDst
            return
        }
        copy(dst[nDst:], src[nSrc:nSrc+width])
        nDst += width
        nSrc += width
    }
    return
}

func main() {
    transliterations := AccentsTransformer{'Æ': "E", 'Ø': "OE"}
    testString := "cØØl beÆns"
    b := transform.NewReader(bytes.NewBufferString(testString), transliterations)
    scanner := bufio.NewScanner(b)
    scanner.Split(bufio.ScanWords)
    for scanner.Scan() {
        fmt.Println("token:", scanner.Text())
    }
}

将变压器连接在一起变得非常容易。因此,例如,如果我们想要从输入流中删除所有连字符,只需要恰当地使用transform.Chain

func main() {
    transliterations := AccentsTransformer{'Æ': "E", 'Ø': "OE"}
    removeHyphens := transform.RemoveFunc(func(r rune) bool {
        return r == '-'
    })
    allTransforms := transform.Chain(transliterations, removeHyphens)

    testString := "cØØl beÆns - the next generation"
    b := transform.NewReader(bytes.NewBufferString(testString), allTransforms)
    scanner := bufio.NewScanner(b)
    scanner.Split(bufio.ScanWords)
    for scanner.Scan() {
        fmt.Println("token:", scanner.Text())
    }
}

我没有详尽地测试上面的代码,所以请不要在没有充分测试的情况下复制并粘贴它。 :P我刚刚把它煮熟了。但是这种方法 - 避免整个文件读取 - 会更好地扩展,因为它会以块的形式读取文件。

答案 1 :(得分:0)

1“list.txt”和“词典”有多大?如果它太大了,难怪记忆力如此之大

 pibns := bytes.Fields(data)

len(pibns)多少钱?

2启动gc debug(执行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()
      }
    .......