猿问

使用 Echo 或 Gin 框架的大型数组的内存消耗

当我尝试用 Echo(还有 Gin)发送一个大数组时,我遇到了内存问题。请求后,内存并未释放。


package main


import (

    "net/http"

    "strconv"


    "github.com/labstack/echo"

)


type User struct {

    Username  string

    Password  string

    Lastname  string

    Firstname string

}


func main() {

    e := echo.New()

    e.GET("/", func(c echo.Context) error {

        var user User

        users := make([]User, 0)


        for i := 0; i < 100000; i++ {

            user = User{

                Username:  "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),

                Password:  "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhftd",

                Lastname:  "njuftydfhgjkjlkjlkjlkhjkhu",

                Firstname: "jkggkjkl,,lm,kljkvgf"}


            users = append(users, user)

        }


        defer func() {

            users = nil

        }()

        return c.JSON(http.StatusOK, users)

    })

    e.Logger.Fatal(e.Start(":1323"))

}

为了测试,我并行运行请求,得到以下结果:

  • 1 个请求:300Mo

  • 5 个请求:1.5Go

  • 10 个请求:3.1Go

  • 更多:我的电脑死机了:)

如何减少内存消耗?


桃花长相依
浏览 150回答 1
1回答

森林海

分配的内存不会立即返回给操作系统,您的答案几乎没有改进,因为您仍在内存中构建(Go)数组(或更确切地说切片),并且一旦完成,您才可以继续将其编组到响应中。您还可以为每个项目创建一个新的编码器,编组单个项目,然后将其丢弃。您可以用来json.Encoder整理多个项目。您还可以在每个项目之后刷新响应,这也是非常低效的。这违背了所有内部缓冲的目的......相反,您可以在项目 ( User) 准备好后立即对它们进行编组,这样您就不必将所有内容都保留在内存中。并且不要在每个用户之后刷新,最后执行一次就足够了,这是没有必要的,因为一旦您从处理程序返回,服务器将刷新所有缓冲的数据。这样做:e.GET("/", func(c echo.Context) error {    c.Response().WriteHeader(http.StatusOK)    enc := json.NewEncoder(c.Response())    for i := 0; i < 100000; i++ {        user := User{            Username:  "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),            Password:  "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhfd",            Lastname:  "njuftydfhgjkjlkjlkjlkhjkhu",            Firstname: "jkggkjkl,,lm,kljkvgf",        }        if err := enc.Encode(user); err != nil {            return err        }    }    return nil})这里需要注意一点:上面的代码不会将 JSON 数组发送到输出,而是发送一系列 JSON 对象。如果这不适合您并且您确实需要发送单个 JSON 数组,只需“框架”数据并在项目之间插入逗号:e.GET("/", func(c echo.Context) error {    resp := c.Response()    resp.WriteHeader(http.StatusOK)    if _, err := io.WriteString(resp, "["); err != nil {        return err    }    enc := json.NewEncoder(resp)    for i := 0; i < 100000; i++ {        if i > 0 {            if _, err := io.WriteString(resp, ","); err != nil {                return err            }        }        user := User{            Username:  "ffgfgfghhfghfhgfgfhgfghfghfhgfhgfh" + strconv.Itoa(i),            Password:  "gjgjghjgjhgjhghjfrserhkhjhklljjkbhjvftxersgdghjjkhkljkbhft",            Lastname:  "njuftydfhgjkjlkjlkjlkhjkhu",            Firstname: "jkggkjkl,,lm,kljkvgf",        }        if err := enc.Encode(user); err != nil {            return err        }    }    if _, err := io.WriteString(resp, "]"); err != nil {        return err    }    return nil})
随时随地看视频慕课网APP

相关分类

Go
我要回答