为什么这个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(){
索引()
}
Mygo 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)]
全部设置为零(如果您没有或无法接触第二种解决方案):
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
}