关于Golang程序的内存占用过大的问题
之前看Mastering Go 2nd Edition,说到Golang的垃圾回收机制,说垃圾回收器(GC)每5分钟会调用一次。然而我观察服务器内存占用时,发现程序后的空闲器5分钟后,使用top查看内存占用并没有减少,大小是200M。因此开始怀疑起了是否有内存泄漏问题。
然而在使用pprof分析后,发现其报告的常驻内存占用只有10M,inuse_objects相对更多,也不过40M。goroutine没有泄漏,makeslice确实调用得非常多,但也仅仅是当时占用一下内存后来都释放了。有时候会有空闲的TCP连接,最后也被关闭了。可疑的代码都检查了一遍,最后认为只可能给内存造成压力但不应该内存泄漏。所以top查看的200M是怎么来的?
在我再也挖不出任何可能性时,直到看到这篇文章,经历几乎一模一样。并且写得非常清楚有逻辑,以至于我没有想再完整记录一遍我的经历的想法。这篇文章甚至详细到了解释每一步的pprof的操作和术语,调试过程非常值得学习。
最后结论就是,没有内存泄漏。Golang的垃圾回收机制在1.12后变了,不再是5分钟机制(每5分钟发送MADV_DONTNEED
信号),而变成Go会告诉系统有空闲的内存可以回收(发送MADV_FREE
信号),但什么时候把内存还给系统是需要系统决定。如果系统内存一直有空闲就可能一直不会还系统,导致top显示出的内存占用一直很大。
文章中还指出,如果想用之前那样的5分钟回收机制,运行时可以加上flagGODEBUG=madvdontneed=1
。像之前那样使用runtime.GC
与runtime/debug
中的FreeOSMemory
都没有效果,也就是不会发送MADV_DONTNEED
信号。
后来我也去查看了服务器的内存情况,发现,在一个小时后(程序这期间一直是空闲状态),内存终于降了100M,变得和之间相同了。确实是没有内存泄漏,纯属服务器闲的。
知道这一点后我的内存洁癖也算是消失了。因为一直非常讨厌高内存占用的应用,觉得是没有优化好。现在看来,这也能有泡沫,反倒是没事就去清一下内存没有必要,重新申请又是额外的开销。从前的(静态)语言确实需要程序员手动管理内存,而go语言在这一点上又帮程序员做了。
“手动强迫系统释放内存的行为都是耍流氓。”