golang 通道内存使用是动态的吗?

我测试了 go channel 内存使用情况,发现它与 channel 输入频率不同,而 goroutines 的数量是相同的。


正如下面的代码,我创建了数千个 goroutine,它们将数据生成到自己的通道并使用来自同一通道的数据。


通过仅更改生产者的变量“间隔”,我可以通过使用命令“top”进行监视来看到虚拟内存和常驻内存也发生变化。


并且间隔越短,内存的使用就越大。


有谁知道会发生什么?


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")

}

有只小跳蛙
浏览 92回答 1
1回答

青春有我

pprof可以告诉你在哪里花费了内存。只需为net/http/pprof包添加一个导入语句,并使用 http.DefaultServeMux 启动一个 HTTP 服务器:import _ "net/http/pprof"func main() {    go func() { log.Fatal(http.ListenAndServe(":4000", nil)) }()    //...}在程序运行时,运行 pprof 工具来查看有关程序的各种统计信息。由于您关心内存使用情况,因此堆配置文件(使用中的内存)可能是最相关的。$ go tool pprof -top 10 http://localhost:4000/debug/pprof/heapFetching profile over HTTP from http://localhost:4000/debug/pprof/heapFile: fooBuild ID: 10Type: inuse_spaceTime: Dec 21, 2018 at 12:52pm (CET)Showing nodes accounting for 827.57MB, 99.62% of 830.73MB totalDropped 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.Aftertime.Timer不出所料,您创建的大量stime.After占用了几乎所有正在使用的内存。想一想:使用 250 毫秒的时间间隔,您创建计时器的速度比使用 1 秒的时间间隔快 4 倍。然而,计时器的寿命与间隔不成比例——它恒定为 60 秒。因此,在任何给定时间点,您有 4*60=240 倍以上的计时器处于活动状态。来自 time.After 的文档:等待持续时间过去后,然后在返回的通道上发送当前时间。它等同于 NewTimer(d).C。在计时器触发之前,垃圾收集器不会回收底层计时器。如果效率是一个问题,请改用 NewTimer 并在不再需要计时器时调用 Timer.Stop。因此,为每个创建一个计时器readLoop并重新使用它。您可以通过使用空结构值通道而不是布尔值通道来进一步减少内存使用: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)    }}
打开App,查看更多内容
随时随地看视频慕课网APP