gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-02 08:24:06 25 4
gpt4 key购买 nike

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

我认为也许fmt.XprintY不会重置缓冲区,但我的调试尝试是徒劳的。

package main

import (
"bytes"
"fmt"
"io"
"text/template"
)

type SecWriter struct {
w io.Writer
}

func (s *SecWriter) Write(p []byte) (n int, err error) {
fmt.Println(string(p), len(p), cap(p))

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

s.w.Write(p[:64])
return 64, nil
}

func index() {
exp := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA{{1}}"

b := &bytes.Buffer{}
s := &SecWriter{
w: b,
}


t := template.Must(template.New("index").Parse(exp))
t.Execute(s, nil)

fmt.Println("buf: ", b.String())
}

func main() {
index()
}

我的go env:

set GOARCH=amd64
set GOOS=windows

Go版本

go version go1.12.5 windows/amd64

输出为:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 64 64
1 1 128
buf: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1nfo{SSSSSSSSSSSSSSSSSSSSSSSSSSS} AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

可以看到,内存中变量的部分值:

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

缓冲区泄漏。

最佳答案

没有内存泄漏this证明了这一点。
但有一个issue :对 p := newPrinter() 的调用在 func Fprint(w io.Writer, a . ..interface{}) (n int, err error) 返回空闲内存( slice 的底层数组),而不将其初始化为零(可能是出于性能原因而未初始化 - 我们期望全部为零)。

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

func (s *SecWriter) Write(p []byte) (n int, err error) {
b := p[len(p):cap(p)]
for i := range b {
b[i] = 0
}
fmt.Println(string(p), len(p), cap(p))
// here
tmp := fmt.Sprintln("info{SSSSSSSSSSSSSSSSSSSSSSSSSSS}")
if tmp == "" {
}
s.w.Write(p[:64])
return 64, nil
}
<小时/>
  • 在 Windows (C:\Go\src\fmt\format.go) 或 Linux (/usr/local/go/src/fmt/format.go) 中) 文件第 58 行将缓冲区设置为全零:
  •     b := (*buf)[:cap(*buf)]
    for i := range b {
    b[i] = 0
    }

    此函数内部:

    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
    <小时/>

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

    如果您想监视计算机的内存,请尝试 this :

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

    并更改const genLen = 64尝试this有趣:cap=64 更改为 cap=128(这不是预期的):

    输出:

    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.">>
    <小时/>

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

    在调试 /usr/local/go/src/fmt/print.go 文件第 230 行的第二个代码后,它似乎是 fmt.bufferlength=3cap=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):

    // 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
    }

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

    关于go - 为什么这个 Golang 代码会泄漏内存中变量的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58255555/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com