chai2010 / webp

WebP decoder and encoder for Go (Zero Dependencies).
http://godoc.org/github.com/chai2010/webp
BSD 3-Clause "New" or "Revised" License
512 stars 89 forks source link

memory leak #26

Open yourchanges opened 4 years ago

yourchanges commented 4 years ago

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 commented 4 years ago

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

yourchanges commented 4 years ago

golang 1.13.5 centos 6 64位

nextonr commented 4 years ago

顶,我也发现了这个问题

tonyzhu commented 3 years ago

解决了吗

2rebi commented 3 years ago

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++
    }
}