分配大量数据块时出现巨大的GC性能问题

时间:2014-12-10 09:50:43

标签: go garbage-collection

我刚刚注意到,如果我在programm中分配一个巨大的内存块。 GC会吃掉所有程序时间。

这是POC。

https://gist.github.com/martende/252f403f0c17cb489de4

func main() {
    //////////////// !!!!!!!
    /* If I uncomment 2 lines below programm runs fast */
    nodesPool := make([]int, 300e6, 300e6)
    _ = nodesPool
    //////////////////////7

    file, _ := os.Open("result.txt")
    defer file.Close()

    reader := bufio.NewReader(file)

    var lastLinkIdx = 1 // Dont use first element use 0 as saver

    cnt:=  0
    totalB := 0

    for {
       l, err := reader.ReadString('\n')
       if err == io.EOF {
    fmt.Println("EOF")
        break
    }

    cnt+=1      
    totalB+=len(l)

    lines := strings.Split(l, ":")
    nodeId,_ := strconv.Atoi(lines[0])
    _ = nodeId

    linkIdsStr  := strings.Split(lines[1], ",")
    var ii = len(linkIdsStr)
    _ = ii
    /*      ... */
}

fmt.Println("pool ",cnt,totalB,lastLinkIdx)


}

我认为GC尝试以某种方式移动巨大的内存块,实际上是否可以从GC中分配内存但是将GC留给所有其他库,因为即使ReadLine需要它。

这是内存块的分析。

Total: 1445 samples
     428  29.6%  29.6%      722  50.0% runtime.sweepone
     375  26.0%  55.6%      375  26.0% markroot
     263  18.2%  73.8%      263  18.2% runtime.xadd
     116   8.0%  81.8%      116   8.0% strings.Count
      98   6.8%  88.6%      673  46.6% strings.genSplit
      34   2.4%  90.9%       44   3.0% runtime.MSpan_Sweep
      25   1.7%  92.7%      729  50.4% MCentral_Grow
      17   1.2%  93.8%       19   1.3% syscall.Syscall
       9   0.6%  94.5%        9   0.6% runtime.memclr
       9   0.6%  95.1%        9   0.6% runtime.memmove

这是没有内存块的分析。

  98  27.0%  27.0%       98  27.0% strings.Count
  93  25.6%  52.6%      228  62.8% strings.genSplit
  45  12.4%  65.0%       45  12.4% scanblock
  24   6.6%  71.6%       28   7.7% runtime.MSpan_Sweep
  13   3.6%  75.2%       74  20.4% runtime.mallocgc
  12   3.3%  78.5%       12   3.3% runtime.memclr
   8   2.2%  80.7%        8   2.2% MHeap_ReclaimList
   8   2.2%  82.9%       11   3.0% syscall.Syscall
   6   1.7%  84.6%       44  12.1% MHeap_Reclaim
   6   1.7%  86.2%        6   1.7% markonly

1 个答案:

答案 0 :(得分:1)

Go团队{4}的Dmitry Vyukov says这是一个Go runtime performance issue你可以通过一个巨大的分配来触发,并且作为一种解决方法,“你可以在它成为[s时]立即收集大对象在那之后,就死了并增加GOGC。“

从广义上讲,GitHub问题表明运行时会创建大量的内存管理结构(跨度),然后它会无限期地保留,并且必须扫描每个GC。按照问题标签,针对Go 1.5进行修复。

His sample with workaround是:

package main

import (
    "runtime"
    "runtime/debug"
)

var x = make([]byte, 1<<20)
var y []byte
var z []byte

func main() {
    y = make([]byte, 1<<30)
    y = nil
    runtime.GC()
    debug.SetGCPercent(1000)
    for i := 0; i < 1e6; i++ {
        z = make([]byte, 8192)
    }
}

(有些评论是关于一个完全不同的答案和代码示例,重点是避免我编辑过的分配。没有办法“告诉”StackOverflow这是一个新的答案,所以它们仍然存在。)