编码base64时的内存消耗

memory consumption at encoding base64

我在使用 golangs lib 的软件中遇到内存消耗问题 encoding/base64

我的软件正在将视频文件拆分为单独的图像,(gocv mat) 将它们转换为 base64 字符串并以 json 格式保存到文件中。

在测试过程中,我发现内存使用量一直在增加,直到 oom-reaper 正在终止进程。

使用 pprof 的调查显示 encoding/base64 内存似乎堆积起来。

我在每个图像帧之后做了 pprof 快照,并分配了 mem encoding/base64 在 oom-reaper 终止进程前不久从 976.89kB(平坦)增加到 4633.54kB(平坦)。

Beginning:
      flat  flat%   sum%        cum   cum%
  976.89kB 32.29% 32.29%   976.89kB 32.29%  encoding/base64.(*Encoding).EncodeToString
  512.50kB 16.94% 49.23%   512.50kB 16.94%  runtime.allocm
  512.20kB 16.93% 66.15%   512.20kB 16.93%  runtime.malg
  512.05kB 16.92% 83.08%  1488.94kB 49.21%  runtime.main
     512kB 16.92%   100%      512kB 16.92%  time.resetTimer (inline)
         0     0%   100%   976.89kB 32.29%  main.Process

End:
Showing nodes accounting for 6170.44kB, 100% of 6170.44kB total
      flat  flat%   sum%        cum   cum%
 4633.54kB 75.09% 75.09%  4633.54kB 75.09%  encoding/base64.(*Encoding).EncodeToString
 1024.41kB 16.60% 91.69%  1024.41kB 16.60%  runtime.malg
  512.50kB  8.31%   100%   512.50kB  8.31%  runtime.allocm
         0     0%   100%  4633.54kB 75.09%  main.Process

list 向我展示了与之对应的代码:

(pprof) list encoding/base64
Total: 2.95MB
ROUTINE ======================== encoding/base64.(*Encoding).EncodeToString in /usr/local/go/src/encoding/base64/base64.go
  976.89kB   976.89kB (flat, cum) 32.29% of Total
         .          .    175:
         .          .    176:// EncodeToString returns the base64 encoding of src.
         .          .    177:func (enc *Encoding) EncodeToString(src []byte) string {
         .          .    178:   buf := make([]byte, enc.EncodedLen(len(src)))
         .          .    179:   enc.Encode(buf, src)
  976.89kB   976.89kB    180:   return string(buf)
         .          .    181:}
         .          .    182:
         .          .    183:type encoder struct {
         .          .    184:   err  error
         .          .    185:   enc  *Encoding

所以在我的 golang 代码中,相应的代码行是:

func Process(img gocv.Mat) ( myImage Images  ){

    detectImg, detectClass, detectBoxes := Detect(&net, 
                                           img.Clone(), 
                                           0.45, 0.5, 
                                           OutputNames, classes)
    defer detectImg.Close()

    // convert gocv.Mat to []bytes
    myImg , _ := detectImg.ToImage()
    myJPG := new(bytes.Buffer)
    jpeg.Encode(myJPG, myImg, &jpeg.Options{95})
    myBytes := myJPG.Bytes()


    // memory consuming
    encodedString := base64.StdEncoding.EncodeToString(myBytes)

// [...]

    return myImage

}

“encodedString”在不堆积的情况下如何释放内存? (更新:答案说这没有必要也不可能)

或者这可能不是我的编码错误,内存泄漏是在 lib base64 中?? (更新:答案说肯定不是这种情况)

回答您的问题:

How can I release the memory of "encodedString" in this case that it does not pile up?

你不能也不需要。未使用的内存被“释放”。

Or is it maybe not my wrong coding, and the mem-leak is at the lib base64 ?

没有,包 encoding/base64 没有内存泄漏。 (您在垃圾收集语言的标准库中的一个普通函数中检测到内存泄漏的可能性为 0。)

指导您找到解决方案:

您的应用程序 使用 荒谬的内存量,但那是因为 a) 处理视频和图像 内存饥饿和 b) 你似乎不采取任何措施来保持低内存:例如您将整个图像编码为 bytes.Buffer,然后将整个 bytes.Buffer 编码为一个字符串,然后处理该字符串,依此类推。您可能应该将图像编码为 stream,将此 stream 编码为 base64 并进一步 stream 此输出到它存放的地方。这在 Go 中完全没有痛苦,因为所有这些编码器都在 io.Writers 上工作,可以很容易地链接起来。

我上面的问题是完全错误的。

Base64 根本不是问题,只是内存的 top-consumer 显示在 pprof,导致我得出错误的结论,即 base64 是问题所在。

我猜 pprof 会告诉我所有的事情 mem-consumption 我的 go 程序,包括 gocv。 gocv 是 opencv 的 c 包装器,但它的 mem-consumption 对 pprof 不可见,因为它是 c-code! (我不知道问这个问题)。 由于 pprof 可见的内存消耗未显示 c-wrapper 库使用的内存,比如 gocv。 golang 根本看不到内存消耗的很大一部分。 所以 JimB 的帮助提示是:

Seeing how you are using a Go wrapper around opencv, the memory you are concerned with is probably not even allocated by Go. In that case you do need to ensure that everything is probably closed or released according to their documentation, because the bulk of the work is done in C++, not Go. Even if you are cleaning up properly however, you still need to be aware of your memory limitations and ensure you are not trying to hold too much data at any given point.

当我清理 gocv 对象时,内存消耗显着下降。我曾经关闭对象:

defer obj.close()