golang ioutil.ReadAll引发的内存异常

Cargador · · 3711 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

ioutil.ReadAll 是一个常用的数据读取方法,经常用来读取http请求的response数据,或者读取文件数据。

demo1: http
func handle(r *http.Request, w http.ResponseWriter) {
    file, err := os.Open("tmp.zip")
    // error checks...
    b, err := ioutil.ReadAll(file)
    // error checks
    fmt.FPrintf(w, b)
}
demo2 : 模拟shell md5sum 获取文件MD5
func genmd5(file string) string{
    cf, err := os.Open(file)
    //err check
    defer cf.Close()
    body, err := ioutil.ReadAll(cf)
    //err check
    MD5Str := fmt.Sprintf("%x", md5.Sum(body))
    return MD5Str
}

上述两个例子看起来都没什么问题,但是当文件数据特别大的时候,ioutil.ReadAll会将全部的数据加载到内存,对于demo1,在高并发的情况下,最终会导致服务因为内存不足而崩溃。demo2会将整个文件加载到内存,并且短时无法清理(2G文件生成MD5后,内存大约7min后才会gc完成)。

在这种情况下最好使用io.Copy的方式代替 ioutil.ReadAll

demo1 优化:
func handle(r *http.Request, w http.ResponseWriter) {
    file, err := os.Open("tmp.zip")
    // error checks...
    io.Copy(w, file)
}
Demo2 优化
func genmd5(file string) string{
    f, err := os.Open(file)
    // err check
    defer f.Close()
    // 改用io.Writer对象获取文件数据
    md5hash := md5.New()
    if _, err := io.Copy(md5hash, f); err != nil {
          // err check
    }
    MD5Str := fmt.Sprintf("%x", md5hash.Sum(nil))
    return MD5Str
}

io.Copy使用固定的32K缓冲区,因此无论源数据多大,都只会占用32K内存空间。

所以需要向io.Writer写入时,建议优先使用io.Copy的方式。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Cargador

查看原文:golang ioutil.ReadAll引发的内存异常

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3711 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传