C语言翻车现场!内存哪去了

早上早会后没多久就不断收到涉及短信网关的服务器内存满了的警告信息,由于接口组的开发者已经离职于是安排新来的同事进行排查,收到的反馈是有程序使用了大量的内存,有可能是年底业务量高峰期并发量上涨,但还在查找故障点,收到信息后的第一反应是不可能,这台服务器配置32G的内存,运行的都是接口或者网关类的程序,所以应该是程序有内存泄漏了。同时继续让新同事排查,及时反馈信息。由于没有反馈信息以为问题已经解决但是临近中午的时候就接到了客户投诉说无法收到业务办理的短信。原来同事只是简单重启了一下服务就正常了,去查了源码也迟迟没找出问题。由于年底业务量大,重启之后内存很快就爆满,使得发送缓慢大量消息积累没有及时发送。于是放下手头的工作与新来的同事一起排查,nmon看了一下服务器的情况如下图

C语言翻车现场!内存哪去了

从图中标红部分可以看出名称为web的进程消耗了大概27G的内存

可以看到web的三个进程把内存啃得差不多了,于是通过pmap来查看进程的内存使用情况如下图

C语言翻车现场!内存哪去了

进程的内存情况

发现标识为[ anon ]部分使用了9G左右,我们知道[ anon ]部分是程序在运行中动态分配的部分,因此可以断定程序中有内存泄漏,存在只分配没有回收的BUG,那么问题来了如何定位内存是在哪泄漏的呢?难道要一个个源文件去追踪吗?类似的问题其实只要用诸如splint静态分析工具做一下分析就可以定位出来。于是使用splint分析了下,很快就找到如下的结果提示:有新分配的内存临时使用,但是没有释放

C语言翻车现场!内存哪去了

有新分配的内存临时使用,但是没有释放

查看源码,发现这个函数在进行编码转换时,开发者使用malloc临时分配了缓冲,并且把转码后的数据存入缓冲通过函数返回缓冲地址供外部使用,而调用者使用完后并没有释放。这是一种糟糕的设计,对于调用者来说关心的是功能的使用,但是绝不会关心功能是如何实现的,如果功能的实现还需要使用者进行善后(释放内存),那么也应该是模块里统一的释放,而不是自己直接去free。

C语言区别于其它编程语言的一个显著特点就是提供了强大灵活内存管理功能,使得开发者可以按自己需要进行内存管理。但是在现实的开发环境里,内存管理又往往是开发者的噩梦,一方面涉及程序的内存部分需要精心设计以保周全,另一方面对于使用者来说必须是小心翼翼。涉及内存管理的核心法则是分配和释放必须是一对的。对于项目来说存在一个统一的内存管理的模块,负责内存的统一管理非常必要,在程序中随意直接的使用malloc等分配函数可以说是个糟糕的写法,为什么呢?因为在多次使用分配和释放函数之后,在实际运行中无法提供当前程序使用了多少内存,这些内存的内容是什么?哪些已经是垃圾需要回收?哪些是正常使用?什么时候回收都已经难以知道。有了统一的内存管理模块,以上的问题都轻易解决。

在C语言中涉及内存操作是个让人胆战心惊的事,一不小心就翻车是常有的事,有些甚至翻车后你找不出蛛丝马迹,让你彻夜难眠提心吊胆。缺乏统一的内存管理,malloc满天飞,那么剩下只能是焦头烂额。


分享到:


相關文章: