'memory consumption at encoding base64

I have problems with memory consumption at my software using golangs lib encoding/base64

My software is splitting a videofile to separate images, (gocv mat) converting them to base64 string and saving it to file in json format.

During testing I found that the memory usage is piling up until the oom-reaper is killing the process.

Investigation with pprof showed that the encoding/base64 memory seems to pile up.

I did pprof snapshots after each image frame, and allocated mem of encoding/base64 is raising from 976.89kB(flat) to 4633.54kB(flat) shortly before oom-reaper was killing the process.

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 shows me the code acoording to it:

(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

So in my golang code the according line of code was:

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

}

How can I release the memory of "encodedString" in this case that it does not pile up? (Update: Answers say this is not necessary and not possible)

Or is it maybe not my wrong coding, and the mem-leak is at the lib base64 ?? (Update: Answers say this is surely not the case)



Solution 1:[1]

To answer your questions:

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

You cannot and you need not. Unused memory is "freed".

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

No, package encoding/base64 has no memory leak. (Chances are 0 that you detect a memory leak in a trivial function in the standard library of a garbage collected language.)

To guide you towards a solution:

Your application uses absurd amount of memory but that's because a) processing video and images is memory hungry and b) you seem to do nothing to keep memory low: E.g. you encode the whole image into a bytes.Buffer, then encode the whole bytes.Buffer to a string then work on that string and so on. You probably should encode the image into a stream, encode this stream to base64 and stream this output further to where it deposited. This is totally painless in Go as all these encoders work on io.Writers which can be chained very easily.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Volker