3 回答

TA貢獻1816條經驗 獲得超4個贊
沒有內存泄漏,
但有一個問題p := newPrinter()
:對initialized的調用p.fmt.init(&p.buf)
返回func Fprint(w io.Writer, a ...interface{}) (n int, err error)
空閑內存(切片的底層數組),而沒有將其初始化為零(可能是出于性能原因而未初始化 -我們期望全部為零)。
TL;DR:
兩種解決方案:
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
長答案:
您正在觀看超出指定長度的切片數據,并且允許您查看切片容量的切片數據。
您可以將:替換m.Writer.Write(p[:8])
為:m.Writer.Write(p)
,這將使您的代碼正常工作。以下代碼顯示將template.Parse()
模板標記為 3 部分并調用my.Write()
3 次。這里有趣的部分是第二次調用顯示my.Write()
編譯器生成的具有不同切片容量的切片,該切片未初始化為零,“也許這是一個無害的小問題”:
如果您想監視計算機的內存,請嘗試以下操作:
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嘗試進行以下 有趣的更改: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)。plen=3cap=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):
// 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
}
獲取并返回可用內存,而不將其初始化為零。

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

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