- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
此代码可能会泄漏内存中变量的值。
我认为也许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
}
<小时/>
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=3
和 cap=128
。
在调试 /usr/local/go/src/fmt/print.go
文件第 230 行的第二个代码后,它似乎是 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)
:
// 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/
COW 不是奶牛,是 Copy-On-Write 的缩写,这是一种是复制但也不完全是复制的技术。 一般来说复制就是创建出完全相同的两份,两份是独立的: 但是,有的时候复制这件事没多大必要
我是一名优秀的程序员,十分优秀!