Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/performance/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么这个Golang代码会泄漏内存中变量的值_Go_Memory_Slice - Fatal编程技术网

为什么这个Golang代码会泄漏内存中变量的值

为什么这个Golang代码会泄漏内存中变量的值,go,memory,slice,Go,Memory,Slice,此代码可能会泄漏内存中变量的值 我想可能fmt.XprintY没有重置缓冲区,但我的调试尝试是徒劳的 主程序包 进口( “字节” “fmt” “io” “文本/模板” ) 类型SecWriter结构{ 作家 } func(s*SecWriter)写入(p[]字节)(n int,err error){ 格式打印LN(字符串(p)、len(p)、cap(p)) //这里 tmp:=fmt.Sprintln(“信息{sssssss}”) 如果tmp==“{}” s、 w.Write(p[:64]) 返

此代码可能会泄漏内存中变量的值

我想可能
fmt.XprintY
没有重置缓冲区,但我的调试尝试是徒劳的

主程序包
进口(
“字节”
“fmt”
“io”
“文本/模板”
)
类型SecWriter结构{
作家
}
func(s*SecWriter)写入(p[]字节)(n int,err error){
格式打印LN(字符串(p)、len(p)、cap(p))
//这里
tmp:=fmt.Sprintln(“信息{sssssss}”)
如果tmp==“{}”
s、 w.Write(p[:64])
返回64,零
}
func索引(){
exp:=“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b:=&bytes.Buffer{}
s:=&SecWriter{
w:b,
}
t:=template.Must(template.New(“index”).Parse(exp))
t、 执行(无)
fmt.Println(“buf:,b.String())
}
func main(){
索引()
}
My
go env

set GOARCH=amd64
set GOOS=windows
围棋版

go version go1.12.5 windows/amd64
输出为:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 64 64
1 1 128
buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1nfo{SSSSSSSSSSSSSSSSSSSSSSSSSSS}                 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
如您所见,内存中变量的部分值:

tmp := fmt.Sprintln("info{SSSSSSSSSSSSSSSSSSSSSSSSSSS}")

泄漏到缓冲区。

在Go中,表达式
s.w.Write(p[:64])
可以将切片扩展到其长度之外而不会出错(直到切片的容量)。在本例中,提供的缓冲区长度仅为1,但您将其扩展为64(如输出的第二行所示)。额外63字节中的内容未定义,它恰好是一些
fmt
方法的输出


解决方案是检查切片的长度。如果您想使切片万无一失并确保无法看到超出其长度的内容,您可以使用切片的三个索引语法来设置其容量,例如
p=p[::len(p)]

如果您直接分配给变量而不是执行
fmt.Sprintln
,则变量不会泄漏

代码:

所以我相信是fmt.Sprintln造成了泄漏。此函数调用另一个未报告的函数
newPrinter
,以获取
printer
,该函数反过来维护自己的池和缓存。我还不够深入,但我猜您手动创建的缓冲区可能会在这里重叠/共享


(如果我发现任何其他问题,我将更新答案)

没有内存泄漏,证明了这一点。
但是在
func Fprint(w io.Writer,a…interface{})(n int,err error)
中有一个:调用
p:=newPrinter()
在这里初始化
p.fmt.init(&p.buf)
返回空闲内存(片的底层数组),而不将其初始化为零(这可能是由于性能原因而没有初始化-,我们希望它全部为零

TL;DR:
两种解决方案:
1.解决方法:使用
s.w.Write(p)
而不是
s.w.Write(p[:64])
,或者编辑代码并将
p[len(p):cap(p)]
全部设置为零(如果您没有或无法接触第二种解决方案):


  • 在Windows(
    C:\Go\src\fmt\format.Go
    )或Linux(
    /usr/local/Go/src/fmt/format.Go
    )文件的第58行,将缓冲区设置为零:
  • 在该功能中:

    func (f *fmt) init(buf *buffer) {
        b := (*buf)[:cap(*buf)]
        for i := range b {
            b[i] = 0
        }
        f.buf = buf
        f.clearflags()
    }
    
    您的代码输出已应用此选项:

    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 64 64
    1 1 128
    buf:  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1
    

    长答案:
    您正在查看超出指定长度的切片数据,您可以查看切片容量的切片数据。
    您可以将:
    m.Writer.Write(p[:8])
    替换为:
    m.Writer.Write(p)
    ,这样可以使代码正常工作。下面的代码显示
    template.Parse()
    将模板标记为3个部分,并调用
    my.Write()
    三次。这里有趣的部分是对
    my.Write()
    的第二次调用显示了编译器生成的具有不同片容量的片,该片容量未初始化为零,“可能这是一个小无害”:

    如果要监视计算机内存,请尝试:


    t.Execute(my,my)
    调用
    func(m*myWriter)Write(p[]byte)
    ,因此
    p
    具有由tamplate引擎生成的
    len=3
    cap=128

    在调试了第230行的
    /usr/local/go/src/fmt/print.go
    文件中的第二个代码后,它似乎是
    fmt.buffer
    ,具有
    length=3
    cap=128
    ,如下:

    func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
        p := newPrinter()
        p.doPrint(a)
        n, err = w.Write(p.buf)
        p.free()
        return
    }
    
    调用
    p:=newPrinter()
    在此处初始化
    p.fmt.init(&p.buf)


    获取并返回可用内存,但不将其初始化为零。

    我尝试在没有
    Sprintln
    的情况下运行,但它似乎已删除
    “info{ssssssss}”
    部分来自输出。是的,所以我认为这是关于XprintY函数的缓冲区。当使用
    fmt.Println
    打印到stdout时,值也会泄漏。似乎只有
    printer.buf
    会与局部变量
    p[:64]共享内存。
    我知道tamplate引擎会识别“Hi{.Name}}Bye”到
    TextNode、ActionNode、TextNode
    ,将写入3次。但是,在您的代码中,第二次写入时:
    []字节(“50 2222”)
    在局部变量
    p
    中,它在上一个循环中被写入到stdout中。所以我认为这不是因为模板使用的切片没有初始化。
    t.Execute(my,my)
    调用
    func(m*myWriter)Write(p[]字节)
    p和
    lenaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAA1
    很抱歉,我没有及时回复您。感谢您的努力!如果我理解正确:在第一次调用
    myWriter.Write
    时,fmt.Sprint将缓冲区设置为“info{ssss}”,然后在第二次调用时,fmt.printer获得
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 64 64
    1 1 128
    buf:  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1
    
    package main
    
    import (
        "bytes"
        "fmt"
        "io"
        "text/template"
    )
    
    func main() {
        buf := &bytes.Buffer{}
        my := &myWriter{"You", buf}
        template.Must(template.New("my").Parse("Hi{{.Name}}Bye.")).Execute(my, my)
        fmt.Printf("<<%q>>\n", buf.String())
    }
    func (m *myWriter) Write(p []byte) (n int, err error) {
        fmt.Printf("len=%v cap=%v\t%v %v\n", len(p), cap(p), string(p), p[:cap(p)])
        no++
        fmt.Println("gen:", no, gen())
        m.Writer.Write(p)
        // m.Writer.Write(p[:8])
        return 8, nil
    }
    
    type myWriter struct {
        Name string
        io.Writer
    }
    
    const genLen = 8
    
    func gen() string {
        b := [genLen]byte{}
        for i := range b {
            b[i] = no
        }
        return string(b[:])
    }
    
    var no = byte(49) //'1'
    
    len=2 cap=8 Hi [72 105 0 0 0 0 0 0]
    gen: 50 22222222
    len=3 cap=64    You [89 111 117 58 32 53 48 32 50 50 50 50 50 50 50 50 10 50 32 49 48 53 32 48 32 48 32 48 32 48 32 48 32 48 93 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    gen: 51 33333333
    len=4 cap=8 Bye. [66 121 101 46 0 0 0 0]
    gen: 52 44444444
    <<"HiYouBye.">>
    
    len=2 cap=8 Hi [72 105 0 0 0 0 0 0]
    gen: 50 2222222222222222222222222222222222222222222222222222222222222222
    len=3 cap=128   You [89 111 117 58 32 53 48 32 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
    gen: 51 3333333333333333333333333333333333333333333333333333333333333333
    len=4 cap=8 Bye. [66 121 101 46 0 0 0 0]
    gen: 52 4444444444444444444444444444444444444444444444444444444444444444
    <<"HiYouBye.">>
    
    func Fprint(w io.Writer, a ...interface{}) (n int, err error) {
        p := newPrinter()
        p.doPrint(a)
        n, err = w.Write(p.buf)
        p.free()
        return
    }
    
    // newPrinter allocates a new pp struct or grabs a cached one.
    func newPrinter() *pp {
        p := ppFree.Get().(*pp)
        p.panicking = false
        p.erroring = false
        p.wrapErrs = false
        p.fmt.init(&p.buf)
        return p
    }