go cmd/vet:检测到Get/Post http.Response赋值给"_",这是一个内存泄漏,

sycxhyv7  于 6个月前  发布在  Go
关注(0)|答案(6)|浏览(43)

我犯了一个新手错误,在代码库中引入了内存泄漏,因为我没有意识到即使不需要响应内容,也必须关闭 http.Response 返回的 body。
我怀疑我不是唯一犯这个错误的人。
如果提案被接受,我有一个差异。

1cklez4t

1cklez4t1#

尽管忽略响应对于Post请求来说是常见的,但对于Get请求来说应该是罕见的。因此,如果我们想要一个通用的解决方案,仅仅检测到响应被忽略(""正如你的标题所暗示的)对于Post请求是有帮助的。换句话说,Get请求的主要用例需要查看响应,因此检测""对绝大多数Get请求案例没有帮助。

如果一般情况(*响应是一个函数调用的返回值,且该响应的主体没有关闭)难以检测,那么将这个问题的范围缩小到Post(和Do)可能会更好。

这里有一个特别糟糕的不关闭主体的例子:Azure/azure-sdk-for-go@475dde0#diff-08eb04d438c2ee00895ac528dc7790b5L112。*http.Response是从函数中返回的。重试循环正在覆盖resp,导致它泄漏而没有关闭。但是解决这个特定的例子是有一定难度的——仍然可以从检测常规情况中获得巨大的价值。

我很惊讶这个问题之前没有被讨论过;显然是有用的。尽管在文档中很清楚,但我看到新手和老手都遭受了这个问题。我在github上没有看到讨论——也许其他地方有讨论。与此相关。
cc @bradfitz

rkue9o1l

rkue9o1l2#

还有一个更一般的问题,那就是一旦构造了 io.Closer ,就不应该再使用它。也就是说,永远不要在上面调用 Close()
如果我们只检查 _, err := http.Post(...) 模式,在我看来,这个检查可能不会非常强大。如果我们首先构造 http.Request ,然后在其上调用 Do 呢?如果我们不忽略 http.Response ,但在某些情况下不关闭它呢?上面链接的 Azure bug 具有这两个属性。
另一方面,如果我们实现更通用的检查,我猜想避免误报会非常棘手。例如,一些 io.Closer 的实现可能是无操作的,可以忽略。

ijxebb2r

ijxebb2r4#

例如,io.Closer的一些实现可能是无操作的,可以忽略。
在标准库中甚至有一个这样的辅助函数(https://golang.org/pkg/io/ioutil/#NopCloser),用于将简单的io.Reader转换为具有无操作Close()方法的ReadCloser。

gzjq41n4

gzjq41n45#

我们已经在vet中有一个函数结果不容忽视的列表;听起来像是http.Post应该在那个列表上。
解决这类问题的一般分析被称为类型-状态验证。例如,它会检查由Open返回的文件上的所有Read和Write调用是否在Close之前发生,以及所有控制流路径上是否都发生了Close调用(较宽松的检查版本将确保至少某些路径上调用了Close)。这种分析的质量取决于它如何精确地模拟数据、控制和调用图。通常,在更复杂的情况中,需要大量的分析能力(以及CPU/RAM)才能做好工作,但我们可以用更简单的启发式方法来捕捉愚蠢的错误。一个可以遵循的例子可能是vet中的lostcancel检查,它确保在创建上下文的取消函数后,该函数在所有控制流路径上都被“使用”。这个检查或许可以推广以解决其他此类问题。

lawou6xi

lawou6xi6#

如果我们将 -closer 作为 vet 标志,当忽略本地的 io.Closer 时会报错,那么我们就可以从 stdlib 中白名单 ioutil.nopCloser。这是默认检查的一个开始。

在 vet 中,我们已经有了一个不应被忽略结果的函数列表;听起来 http.Post 应该在这个列表里。

你能分享一下这个位置吗?

相关问题