Memory management 多部分表单上传+;golang内存泄漏?

Memory management 多部分表单上传+;golang内存泄漏?,memory-management,memory-leaks,go,Memory Management,Memory Leaks,Go,以下服务器代码: package main import ( "fmt" "net/http" ) func handler(w http.ResponseWriter, r *http.Request) { file, _, err := r.FormFile("file") if err != nil { fmt.Fprintln(w, err) return } defer file.Close() return } func main(

以下服务器代码:

package main

import (
  "fmt"
  "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
  file, _, err := r.FormFile("file")
  if err != nil {
    fmt.Fprintln(w, err)
    return
  }
  defer file.Close()

  return
}

func main() {
  http.ListenAndServe(":8081", http.HandlerFunc(handler))
}
正在运行,然后使用以下命令调用它:

curl -i -F "file=@./large-file" --form hello=world http://localhost:8081/
在darwin/amd64和linux/amd64上的Go 1.4.2中,
大文件大约80MB似乎存在某种形式的内存泄漏

当我连接
pprof
时,我看到
bytes.makeSlice
在调用服务几次后使用了96MB的内存(最终在我上面的代码中被
r.FormFile
调用)

如果我继续调用
curl
,进程的内存使用会随着时间的推移而增长,最终似乎会在我的机器上停留在300MB左右


想法?我假设这不是预期的/我做错了什么?

如果内存使用停滞在“最大值”,我不会真的称之为内存泄漏。我更愿意说GC不是很热心,也不是很懒。或者只是不想在频繁重新分配/需要内存时物理释放内存。如果这真的是内存泄漏,那么使用的内存不会停止在300MB

r.FormFile(“file”)
将导致调用,32MB将用作
maxMemory
参数的值(在
request.go
中定义的
defaultMaxMemory
变量的值)。由于您上载了一个更大的文件(80MB),因此将创建一个大小至少为32MB的缓冲区-最终(这在中实现)。由于用于读取内容,读取过程将从一个小的或空的缓冲区开始,并在需要更大的缓冲区时重新分配

缓冲区重新分配的策略和缓冲区大小取决于实现(也取决于从请求中读取/解码的块的大小),但为了获得一个粗略的图像,请想像如下:0字节、4 KB、16 KB、64 KB、256 KB、1 MB、4 MB、16 MB、64 MB。同样,这只是理论上的,但它说明,只要读取内存中文件的前32MB,总和甚至可以超过100MB,此时将决定将其移动/存储到文件中。有关详细信息,请参见的实现。这合理地解释了96MB的分配

这样做几次,在GC不立即释放分配的缓冲区的情况下,很容易得到300MB。如果有足够的空闲内存,GC就不会有压力急于释放内存。您看到它变得相对较大的原因是因为在后台使用了较大的缓冲区。如果你上传一个1MB的文件,你可能不会遇到这种情况

如果它对您很重要,您还可以使用较小的
maxMemory
值手动调用
Request.ParseMultipartForm()
,例如

r.ParseMultipartForm(2 << 20) // 2 MB
file, _, err := r.FormFile("file")
// ... rest of your handler

r.ParseMultipartForm(2)在GCed语言中查找内存泄漏是一件棘手的事情。特别是因为内存没有立即返回到操作系统。但我认为可能值得尝试关闭正文。我只是浏览了源代码,没有看到明显的正文关闭。@Volker通常服务器会关闭正文(请参见字段)。我认为这只是“正常"分配行为。虽然自从切换到连续堆栈后我有点过时了。谢谢!我将curl调用放入一个循环中,并看到它的上限为300,并且永远不会超过这个上限。我在尝试强制它运行时将其视为GC行为,但我猜即使引用已被删除,GC也不会总是释放内存释放。