Golang defer导致内存溢出
Golang中提供了defer关键字,以前可能需要在函数内进行多次判断之后释放资源,现在使用defer一次就搞定了,习惯之后会觉得这个特性非常好用。
问题代码
有一句话是软件设计没有银弹
,放在这里依然成立。defer这么好用,但也需要注意一些坑。代码逻辑大致如下:
for {
defer 释放资源
// http request
// 错误码判断
// sleep 5s重试 或 break退出
}
业务是心跳这样的守护进程,所以直接就用了死循环,请求失败之后进行重试,或满足一定条件时退出循环。使用defer在最后释放资源。
测试时异常场景也进行了覆盖,功能上没有发现问题。但在一个实例上发现运行一段时间之后内存爆了。
问题分析
defer的实现机制是在调用函数栈上再压入defer函数,调用return前调用defer函数。所以调用defer会有一次压栈操作。
上面的示例中将defer在for循环中,环境恰好没有返回对应的错误码让for循环退出,导致defer一直被执行,重复的压栈导致StackOverflow。
小结
从这里看,将defer放入循环中就是一个不好的习惯;另外,死循环代码也有一丝丝坏味道,看到这样的代码时就应该警惕了。