webp icon indicating copy to clipboard operation
webp copied to clipboard

memory leak

Open yourchanges opened this issue 4 years ago • 5 comments

I create a simple http server do convert a jpg to webp

here is the sample code:

func ImgTowebp(imgData []byte) ([]byte, error) {
	//jpeg.Decode()
	img, err := jpeg.Decode(bytes.NewBuffer(imgData))
	if err != nil {
		//log.Println("decode error from jpg",err)
		img, err = png.Decode(bytes.NewBuffer(imgData))
		if err != nil {
			log.Println("decode error from png or jpg", err)
			return nil, err
		}
	}
	//log.Println(fname)
	webImgBytes, err := webp.EncodeRGBA(img, 80)
	if err == nil {
		webpLength := len(webImgBytes)
		imgLength := len(imgData)
		if webpLength > imgLength {
			log.Println("webp bigger than img")
		}
		log.Println("img2webp <webp size>:<img size>", webpLength, ":", imgLength)
		return webImgBytes, nil
	} else {
		log.Println("webp encode jpg file", err)
		return nil, err
	}

}


main.go

webImgBytes, err := ImgTowebp(bodybuffer)
			if err == nil {
				w.WriteHeader(http.StatusOK)
				w.Header().Set("Content-Type", "image/webp")
				w.Write(webImgBytes)
				return
			} 

运行一段时间后, 内存分析图:

20191217141007

查看 相关源代码:

func EncodeRGBA(m image.Image, quality float32) (data []byte, err error) {
	p := toRGBAImage(m)
	data, err = webpEncodeRGBA(p.Pix, p.Rect.Dx(), p.Rect.Dy(), p.Stride, quality)
	return
}

func toRGBAImage(m image.Image) *image.RGBA {
	if m, ok := m.(*image.RGBA); ok {
		return m
	}
	b := m.Bounds()
	rgba := image.NewRGBA(b)
	dstColorRGBA64 := &color.RGBA64{}
	dstColor := color.Color(dstColorRGBA64)
	for y := b.Min.Y; y < b.Max.Y; y++ {
		for x := b.Min.X; x < b.Max.X; x++ {
			pr, pg, pb, pa := m.At(x, y).RGBA()
			dstColorRGBA64.R = uint16(pr)
			dstColorRGBA64.G = uint16(pg)
			dstColorRGBA64.B = uint16(pb)
			dstColorRGBA64.A = uint16(pa)
			rgba.Set(x, y, dstColor)
		}
	}
	return rgba
}

func webpEncodeRGB(pix []byte, width, height, stride int, quality float32) (output []byte, err error) {
	if len(pix) == 0 || width <= 0 || height <= 0 || stride <= 0 || quality < 0.0 {
		err = errors.New("webpEncodeRGB: bad arguments")
		return
	}
	if stride < width*3 && len(pix) < height*stride {
		err = errors.New("webpEncodeRGB: bad arguments")
		return
	}

     //主要怀疑 (*C.uint8_t)(unsafe.Pointer(&pix[0])) 这个是golang分配的图片pix数组, 传递到CGO后,没有进行释放,就一直持有。
///
	var cptr_size C.size_t
	var cptr = C.webpEncodeRGB(
		(*C.uint8_t)(unsafe.Pointer(&pix[0])), C.int(width), C.int(height),
		C.int(stride), C.float(quality),
		&cptr_size,
	)
	if cptr == nil || cptr_size == 0 {
		err = errors.New("webpEncodeRGB: failed")
		return
	}
	defer C.free(unsafe.Pointer(cptr))

	output = make([]byte, int(cptr_size))
	copy(output, ((*[1 << 30]byte)(unsafe.Pointer(cptr)))[0:len(output):len(output)])
	return
}

yourchanges avatar Dec 17 '19 06:12 yourchanges

图是heap inuse_space, heap 对象数 一直加,没见降

yourchanges avatar Dec 17 '19 06:12 yourchanges

golang 1.13.5 centos 6 64位

yourchanges avatar Dec 17 '19 06:12 yourchanges

顶,我也发现了这个问题

nextonr avatar Apr 30 '20 13:04 nextonr

解决了吗

tonyzhu avatar Jul 30 '20 13:07 tonyzhu

Test Environments

go version

go version go1.13.3 darwin/amd64

go env

GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="==security_stuff=="
GOENV="==security_stuff=="
GOEXE=""
GOFLAGS=" -mod="
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="==security_stuff=="
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="==security_stuff=="

Summary

I did profiling to next code and the result, I got chart of the memory allocated. So i think this library haven't memory leak it's my concluding.

newplot (1)

package main

import (
	"bytes"
	"fmt"
	"github.com/chai2010/webp"
	_ "github.com/mkevac/debugcharts"
	"image/jpeg"
	"io/ioutil"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"
)

func main() {
	go func() {
		http.ListenAndServe(":3030", nil)
	}()


	i := 0
	for {
		time.Sleep(time.Second / 10)

		imgData, err := ioutil.ReadFile("test2.jpeg")
		img, err := jpeg.Decode(bytes.NewBuffer(imgData))
		if err != nil {
			log.Println(err)
		}

		output, err := webp.EncodeRGBA(img, 80)
		if err != nil {
			log.Println(err)
		}
		if output != nil {

		}

		fmt.Println("Save output.webp ok", i)
		i++
	}
}

2rebi avatar Oct 21 '20 04:10 2rebi