go - 大分配数据 block 的巨大 GC 性能问题

标签 go garbage-collection

我刚刚注意到,如果我在程序中分配一个巨大的内存块。 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

最佳答案

Go 团队的 Dmitry Vyukov says这是Go runtime performance issue你可以用一个巨大的分配来触发,作为一种解决方法,“你可以在大对象死亡后立即收集它,然后立即增加 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 这是一个新答案,所以它们保留了下来。)

关于go - 大分配数据 block 的巨大 GC 性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27397844/

相关文章:

go - 显示结构值的链代码函数

python - 对于 Python 中的 gc,动态创建的类是否始终为 "unreachable"?

Java 8 元空间 : OutOfMemoryError: Dealing with reflection Inflation

memory-leaks - 禁用D的GC是 'ok'吗?

xml - GOLANG xml解码

Golang BigQuery - 处理包含重复字段的行并将它们 slice

select - 我可以在不等待 goroutine 返回的情况下强制终止它吗?

go - 附加两个数组的通用函数

java - Apache POI 超出了 GC 开销限制

c# - .NET 中的内存分配