golang ioutil.ReadAll引發(fā)的內(nèi)存異常

ioutil.ReadAll 是一個常用的數(shù)據(jù)讀取方法,經(jīng)常用來讀取http請求的response數(shù)據(jù),或者讀取文件數(shù)據(jù)。

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
}

上述兩個例子看起來都沒什么問題,但是當(dāng)文件數(shù)據(jù)特別大的時候,ioutil.ReadAll會將全部的數(shù)據(jù)加載到內(nèi)存,對于demo1,在高并發(fā)的情況下,最終會導(dǎo)致服務(wù)因?yàn)閮?nèi)存不足而崩潰。demo2會將整個文件加載到內(nèi)存,并且短時無法清理(2G文件生成MD5后,內(nèi)存大約7min后才會gc完成)。

在這種情況下最好使用io.Copy的方式代替 ioutil.ReadAll

demo1 優(yōu)化:
func handle(r *http.Request, w http.ResponseWriter) {
    file, err := os.Open("tmp.zip")
    // error checks...
    io.Copy(w, file)
}
Demo2 優(yōu)化
func genmd5(file string) string{
    f, err := os.Open(file)
    // err check
    defer f.Close()
    // 改用io.Writer對象獲取文件數(shù)據(jù)
    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緩沖區(qū),因此無論源數(shù)據(jù)多大,都只會占用32K內(nèi)存空間。

所以需要向io.Writer寫入時,建議優(yōu)先使用io.Copy的方式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容