Golang:致命错误:运行时:内存不足

Dav*_*vid 0 linux memory-management out-of-memory go

我试图在Github中使用这个包来进行字符串匹配.我的字典是4 MB.在创建Trie时,我得到了fatal error: runtime: out of memory.我使用的是Ubuntu 14.04,内存为8 GB,Golang版本为1.4.2.

这似乎错误来自于线99(现在)在这里:m.trie = make([]node, max)

程序在此行停止.

这是错误:

fatal error: runtime: out of memory

runtime stack:
runtime.SysMap(0xc209cd0000, 0x3b1bc0000, 0x570a00, 0x5783f8)
    /usr/local/go/src/runtime/mem_linux.c:149 +0x98
runtime.MHeap_SysAlloc(0x57dae0, 0x3b1bc0000, 0x4296f2)
    /usr/local/go/src/runtime/malloc.c:284 +0x124
runtime.MHeap_Alloc(0x57dae0, 0x1d8dda, 0x10100000000, 0x8)
    /usr/local/go/src/runtime/mheap.c:240 +0x66

goroutine 1 [running]:
runtime.switchtoM()
    /usr/local/go/src/runtime/asm_amd64.s:198 fp=0xc208518a60 sp=0xc208518a58
runtime.mallocgc(0x3b1bb25f0, 0x4d7fc0, 0x0, 0xc20803c0d0)
    /usr/local/go/src/runtime/malloc.go:199 +0x9f3 fp=0xc208518b10 sp=0xc208518a60
runtime.newarray(0x4d7fc0, 0x3a164e, 0x1)
    /usr/local/go/src/runtime/malloc.go:365 +0xc1 fp=0xc208518b48 sp=0xc208518b10
runtime.makeslice(0x4a52a0, 0x3a164e, 0x3a164e, 0x0, 0x0, 0x0)
    /usr/local/go/src/runtime/slice.go:32 +0x15c fp=0xc208518b90 sp=0xc208518b48
github.com/mf/ahocorasick.(*Matcher).buildTrie(0xc2083c7e60, 0xc209860000, 0x26afb, 0x2f555)
    /home/go/ahocorasick/ahocorasick.go:104 +0x28b fp=0xc208518d90 sp=0xc208518b90
github.com/mf/ahocorasick.NewStringMatcher(0xc208bd0000, 0x26afb, 0x2d600, 0x8)
    /home/go/ahocorasick/ahocorasick.go:222 +0x34b fp=0xc208518ec0 sp=0xc208518d90
main.main()
    /home/go/seme/substrings.go:66 +0x257 fp=0xc208518f98 sp=0xc208518ec0
runtime.main()
    /usr/local/go/src/runtime/proc.go:63 +0xf3 fp=0xc208518fe0 sp=0xc208518f98
runtime.goexit()
    /usr/local/go/src/runtime/asm_amd64.s:2232 +0x1 fp=0xc208518fe8 sp=0xc208518fe0
exit status 2
Run Code Online (Sandbox Code Playgroud)

这是主要功能的内容(取自同一个repo:test file)

var dictionary = InitDictionary()   
var bytes = []byte(""Partial invoice (€100,000, so roughly 40%) for the consignment C27655 we shipped on 15th August to London from the Make Believe Town depot. INV2345 is for the balance.. Customer contact (Sigourney) says they will pay this on the usual credit terms (30 days).")   

var precomputed = ahocorasick.NewStringMatcher(dictionary)// line 66 here
fmt.Println(precomputed.Match(bytes))
Run Code Online (Sandbox Code Playgroud)

T. *_*rie 9

你的结构在内存方面非常低效,让我们看一下内部结构.但在此之前,请快速提醒一些go类型所需的空间:

  • 布尔:1个字节
  • int:4个字节
  • uintptr:4个字节
  • [N]类型:N*sizeof(类型)
  • []类型:12 + len(切片)*sizeof(类型)

现在,让我们来看看你的结构:

type node struct {
    root bool        // 1 byte
    b []byte         // 12 + len(slice)*1
    output bool      // 1 byte
    index int        // 4 bytes
    counter int      // 4 bytes
    child [256]*node // 256*4 = 1024 bytes
    fails [256]*node // 256*4 = 1024 bytes
    suffix *node     // 4 bytes
    fail *node       // 4 bytes
}
Run Code Online (Sandbox Code Playgroud)

好吧,你应该猜测这里发生了什么:每个节点重量超过2KB,这是巨大的!最后,我们将查看用于初始化trie的代码:

func (m *Matcher) buildTrie(dictionary [][]byte) {
    max := 1
    for _, blice := range dictionary {
        max += len(blice)
    }
    m.trie = make([]node, max)

    // ...
}
Run Code Online (Sandbox Code Playgroud)

你说你的字典是4 MB.如果它总共是4MB,则意味着在for循环结束时,max = 4MB.它拥有4 MB不同的单词,然后max = 4MB*avg(word_length).

我们将采取第一个场景,最好的场景.您正在初始化一个4M节点,每个节点使用2KB.是的,这需要一个漂亮的8GB.

您应该检查如何构建您的trie.在与Aho-Corasick算法相关的维基百科页面中,每个节点包含一个字符,因此最多有256个字符来自根,而不是4MB.

一些使其正确的材料:http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf


sto*_*ith 5

node类型的内存大小为2084字节。我写了一个小程序来演示内存使用情况:https : //play.golang.org/p/szm7AirsDB

如您所见,三个字符串(大小为 11(+1) 个字节)dictionary := []string{"fizz", "buzz", "123"}需要 24 MB 内存。

如果您的字典长度为 4 MB,您将需要大约4000 * 2084 = 8.1 GB内存。

因此,您应该尝试减小字典的大小。

  • 是的,你说得对,我只是不想写一些类似“这段代码很垃圾,不要使用它”之类的东西。 (3认同)
  • 您不会通过减少其条目的大小来修复损坏的数据结构,而是采用更有效的方式。 (2认同)