3 回答

TA貢獻(xiàn)1816條經(jīng)驗(yàn) 獲得超4個(gè)贊
沒有內(nèi)存泄漏,
但有一個(gè)問題p := newPrinter()
:對initialized的調(diào)用p.fmt.init(&p.buf)
返回func Fprint(w io.Writer, a ...interface{}) (n int, err error)
空閑內(nèi)存(切片的底層數(shù)組),而沒有將其初始化為零(可能是出于性能原因而未初始化 -我們期望全部為零)。
TL;DR:
兩種解決方案:
1. 解決方法:使用s.w.Write(p)
代替s.w.Write(p[:64])
,或編輯代碼并將其p[len(p):cap(p)]
全部設(shè)置為零(如果您不這樣做或無法觸及第二個(gè)解決方案):
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 行將緩沖區(qū)設(shè)置為全零:
? ? b := (*buf)[:cap(*buf)]
? ? for i := range b {
? ? ? ? b[i] = 0
? ? }
在這個(gè)函數(shù)內(nèi)部:
func (f *fmt) init(buf *buffer) {
? ? b := (*buf)[:cap(*buf)]
? ? for i := range b {
? ? ? ? b[i] = 0
? ? }
? ? f.buf = buf
? ? f.clearflags()
}
應(yīng)用此功能后的代碼輸出:
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 64 64
1 1 128
buf:? AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1
長答案:
您正在觀看超出指定長度的切片數(shù)據(jù),并且允許您查看切片容量的切片數(shù)據(jù)。
您可以將:替換m.Writer.Write(p[:8])
為:m.Writer.Write(p)
,這將使您的代碼正常工作。以下代碼顯示將template.Parse()
模板標(biāo)記為 3 部分并調(diào)用my.Write()
3 次。這里有趣的部分是第二次調(diào)用顯示my.Write()
編譯器生成的具有不同切片容量的切片,該切片未初始化為零,“也許這是一個(gè)無害的小問題”:
如果您想監(jiān)視計(jì)算機(jī)的內(nèi)存,請嘗試以下操作:
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嘗試進(jìn)行以下 有趣的更改:cap=64更改cap=128(這是預(yù)期之外的):
輸出:
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)調(diào)用由模板引擎生成func (m *myWriter) Write(p []byte)。plen=3cap=128
在調(diào)試文件內(nèi)第 230 行的第二個(gè)代碼后/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
}
調(diào)用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
}
獲取并返回可用內(nèi)存,而不將其初始化為零。

TA貢獻(xiàn)1797條經(jīng)驗(yàn) 獲得超4個(gè)贊
在 Go 中,表達(dá)式s.w.Write(p[:64])
可以將切片擴(kuò)展到其長度之外而不會(huì)出現(xiàn)錯(cuò)誤(直到切片的容量)。在本例中,提供的緩沖區(qū)長度僅為 1,但您將其擴(kuò)展為 64(如輸出的第二行所示)。額外的63字節(jié)中的內(nèi)容是未定義的,它恰好是某些方法的輸出fmt
。
解決方案是檢查切片的長度。如果你想讓切片萬無一失,保證無法看到超出其長度的內(nèi)容,可以使用切片的三索引語法來設(shè)置其容量,例如p = p[::len(p)]
.

TA貢獻(xiàn)1898條經(jīng)驗(yàn) 獲得超8個(gè)贊
如果直接給變量賦值而不是這樣做fmt.Sprintln
,變量就不會(huì)泄漏。
代碼: https: //play.golang.org/p/Nz0y_MfDjP1
所以我相信fmt.Sprintln
是造成泄漏的原因。該函數(shù)調(diào)用另一個(gè)未導(dǎo)出的函數(shù)newPrinter
來獲取 a ,printer
而該函數(shù)又維護(hù)它自己的池和緩存。我還不夠深入,但我的猜測是,您手動(dòng)創(chuàng)建的緩沖區(qū)可能會(huì)以某種方式在這里重疊/共享。
(如果我發(fā)現(xiàn)其他內(nèi)容,我會(huì)更新答案)
- 3 回答
- 0 關(guān)注
- 198 瀏覽
添加回答
舉報(bào)