C語言翻車現場!內存哪去了

早上早會後沒多久就不斷收到涉及短信網關的服務器內存滿了的警告信息,由於接口組的開發者已經離職於是安排新來的同事進行排查,收到的反饋是有程序使用了大量的內存,有可能是年底業務量高峰期併發量上漲,但還在查找故障點,收到信息後的第一反應是不可能,這臺服務器配置32G的內存,運行的都是接口或者網關類的程序,所以應該是程序有內存洩漏了。同時繼續讓新同事排查,及時反饋信息。由於沒有反饋信息以為問題已經解決但是臨近中午的時候就接到了客戶投訴說無法收到業務辦理的短信。原來同事只是簡單重啟了一下服務就正常了,去查了源碼也遲遲沒找出問題。由於年底業務量大,重啟之後內存很快就爆滿,使得發送緩慢大量消息積累沒有及時發送。於是放下手頭的工作與新來的同事一起排查,nmon看了一下服務器的情況如下圖

C語言翻車現場!內存哪去了

從圖中標紅部分可以看出名稱為web的進程消耗了大概27G的內存

可以看到web的三個進程把內存啃得差不多了,於是通過pmap來查看進程的內存使用情況如下圖

C語言翻車現場!內存哪去了

進程的內存情況

發現標識為[ anon ]部分使用了9G左右,我們知道[ anon ]部分是程序在運行中動態分配的部分,因此可以斷定程序中有內存洩漏,存在只分配沒有回收的BUG,那麼問題來了如何定位內存是在哪洩漏的呢?難道要一個個源文件去追蹤嗎?類似的問題其實只要用諸如splint靜態分析工具做一下分析就可以定位出來。於是使用splint分析了下,很快就找到如下的結果提示:有新分配的內存臨時使用,但是沒有釋放

C語言翻車現場!內存哪去了

有新分配的內存臨時使用,但是沒有釋放

查看源碼,發現這個函數在進行編碼轉換時,開發者使用malloc臨時分配了緩衝,並且把轉碼後的數據存入緩衝通過函數返回緩衝地址供外部使用,而調用者使用完後並沒有釋放。這是一種糟糕的設計,對於調用者來說關心的是功能的使用,但是絕不會關心功能是如何實現的,如果功能的實現還需要使用者進行善後(釋放內存),那麼也應該是模塊裡統一的釋放,而不是自己直接去free。

C語言區別於其它編程語言的一個顯著特點就是提供了強大靈活內存管理功能,使得開發者可以按自己需要進行內存管理。但是在現實的開發環境裡,內存管理又往往是開發者的噩夢,一方面涉及程序的內存部分需要精心設計以保周全,另一方面對於使用者來說必須是小心翼翼。涉及內存管理的核心法則是分配和釋放必須是一對的。對於項目來說存在一個統一的內存管理的模塊,負責內存的統一管理非常必要,在程序中隨意直接的使用malloc等分配函數可以說是個糟糕的寫法,為什麼呢?因為在多次使用分配和釋放函數之後,在實際運行中無法提供當前程序使用了多少內存,這些內存的內容是什麼?哪些已經是垃圾需要回收?哪些是正常使用?什麼時候回收都已經難以知道。有了統一的內存管理模塊,以上的問題都輕易解決。

在C語言中涉及內存操作是個讓人膽戰心驚的事,一不小心就翻車是常有的事,有些甚至翻車後你找不出蛛絲馬跡,讓你徹夜難眠提心吊膽。缺乏統一的內存管理,malloc滿天飛,那麼剩下只能是焦頭爛額。


分享到:


相關文章: