dopgv00024 2018-12-21 09:34
浏览 105
已采纳

golang通道内存使用情况动态吗?

I have test go channel memory usage and found that it differs from channel input frequency while the amount of goroutines are the same.

As the code below, I create thousands of goroutines, which produce data to its own channel and consume data from same channel.

By changing only the variable "interval" of producer, I can see the virtual memory and resident memory varies too by monitoring with command "top".

And the shorter is the interval, the usage of memory is larger.

Does anyone know what happen?

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

type Session struct {
    KeepAlive chan bool
}

var count = 1024 * 8 * 4

var interval = 250 * time.Millisecond //3718.0m 3.587g   1.2m S 224.0 23.1

// var interval = 500 * time.Millisecond //2011.2m 1.923g   1.2m S 118.8 12.4

// var interval = 1 * time.Second   //1124.0m 1.059g   1.1m S  73.0  6.8

func main() {

    var gracefulStop = make(chan os.Signal, 1)
    signal.Notify(gracefulStop, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)

    for i := 0; i < count; i++ {
        go Loop()
    }

    <-gracefulStop
    fmt.Println("gracefulStop")
}

func Loop() (err error) {

    var se *Session
    se = NewSession()

    se.Serve()

    return
}

func NewSession() (s *Session) {

    fmt.Println("NewSession")

    s = &Session{

        KeepAlive: make(chan bool, 1),
    }

    return
}

func (s *Session) Serve() {

    fmt.Println("Serve")

    go s.sendLoop()

    s.readLoop()

    s.Close()

    return
}

func (s *Session) Close() {

    close(s.KeepAlive)
    fmt.Println("Close")
}

// local-------------------------------------------------------

func (s *Session) readLoop() {
    fmt.Println("readLoop")

    sec := time.Duration(1 * time.Minute)

ServerHandlerLoop:
    for {
        select {

        case alive := <-s.KeepAlive:
            if alive == false {
                break ServerHandlerLoop
            }

        case <-time.After(sec):
            fmt.Println("Timeout")
            break ServerHandlerLoop

        }
    }

    fmt.Println("readLoop EXIT")
}

func (s *Session) sendLoop() {

    for {

        s.KeepAlive <- true

        time.Sleep(interval)

    }

    s.KeepAlive <- false

    fmt.Println("ReadMessage EXIT")
}
  • 写回答

1条回答 默认 最新

  • duanaoshu1989 2018-12-21 12:07
    关注

    pprof can tell you where you spend memory. Simply add an import statement for the net/http/pprof package and start an HTTP server with the http.DefaultServeMux:

    import _ "net/http/pprof"
    
    func main() {
        go func() { log.Fatal(http.ListenAndServe(":4000", nil)) }()
    
        //...
    }
    

    While the program is running, run the pprof tool to look at various statistics about your program. Since you are concerned about memory usage, the heap profile (memory-in-use) is probably most relevant.

    $ go tool pprof -top 10 http://localhost:4000/debug/pprof/heap
    Fetching profile over HTTP from http://localhost:4000/debug/pprof/heap
    File: foo
    Build ID: 10
    Type: inuse_space
    Time: Dec 21, 2018 at 12:52pm (CET)
    Showing nodes accounting for 827.57MB, 99.62% of 830.73MB total
    Dropped 9 nodes (cum <= 4.15MB)
          flat  flat%   sum%        cum   cum%
      778.56MB 93.72% 93.72%   796.31MB 95.86%  time.NewTimer
       18.25MB  2.20% 95.92%    18.25MB  2.20%  time.Sleep
       17.75MB  2.14% 98.05%    17.75MB  2.14%  time.startTimer
          11MB  1.32% 99.38%       11MB  1.32%  runtime.malg
           2MB  0.24% 99.62%   798.31MB 96.10%  main.(*Session).readLoop
             0     0% 99.62%   798.31MB 96.10%  main.(*Session).Serve
             0     0% 99.62%    18.25MB  2.20%  main.(*Session).sendLoop
             0     0% 99.62%   800.81MB 96.40%  main.Loop
             0     0% 99.62%    11.67MB  1.40%  runtime.mstart
             0     0% 99.62%    11.67MB  1.40%  runtime.newproc.func1
             0     0% 99.62%    11.67MB  1.40%  runtime.newproc1
             0     0% 99.62%    11.67MB  1.40%  runtime.systemstack
             0     0% 99.62%   796.31MB 95.86%  time.After
    

    Unsurprisingly, the huge amount of time.Timers you are creating with time.After accounts for pretty much all of the memory in use.

    Think about it: With an interval of 250ms you are creating timers 4 times faster than with an interval of 1s. However, the lifetime of the timers is not proportional to the interval -- it is constant at 60 seconds. So at any given point you have 4*60=240 times more timers alive.

    From the docs of time.After:

    After waits for the duration to elapse and then sends the current time on the returned channel. It is equivalent to NewTimer(d).C. The underlying Timer is not recovered by the garbage collector until the timer fires. If efficiency is a concern, use NewTimer instead and call Timer.Stop if the timer is no longer needed.

    So create a single timer per readLoop and re-use it. You can further reduce memory usage by using a channel of empty struct values instead of a channel of booleans:

    type Session struct {
        KeepAlive chan struct{}
    }
    
    func (s *Session) readLoop() {
        fmt.Println("readLoop")
    
        d := 1 * time.Minute
        t := time.NewTimer(d)
    
    loop:
        for {
            select {
            case _, ok := <-s.KeepAlive:
                if !ok {
                    break loop
                }
    
                if !t.Stop() {
                    <-t.C
                }
                t.Reset(d)
    
            case <-t.C:
                fmt.Println("Timeout")
                break loop
            }
        }
    
        fmt.Println("readLoop EXIT")
    }
    
    func (s *Session) sendLoop() {
        defer close(s.KeepAlive)
    
        for {
            s.KeepAlive <- struct{}{}
            time.Sleep(interval)
        }
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 如何在node.js中或者java中给wav格式的音频编码成sil格式呢
  • ¥15 不小心不正规的开发公司导致不给我们y码,
  • ¥15 我的代码无法在vc++中运行呀,错误很多
  • ¥50 求一个win系统下运行的可自动抓取arm64架构deb安装包和其依赖包的软件。
  • ¥60 fail to initialize keyboard hotkeys through kernel.0000000000
  • ¥30 ppOCRLabel导出识别结果失败
  • ¥15 Centos7 / PETGEM
  • ¥15 csmar数据进行spss描述性统计分析
  • ¥15 各位请问平行检验趋势图这样要怎么调整?说标准差差异太大了
  • ¥15 delphi webbrowser组件网页下拉菜单自动选择问题