valgrind 進階-2


支持原創(chuàng),轉(zhuǎn)載請附上原文鏈接


0 引言

前面介紹了valgrind的基礎(chǔ),參見 Valgrind 基礎(chǔ),今天這結(jié)合Valgrind,來講講內(nèi)存泄漏,以及基于Valgrind定位新思、分析內(nèi)存泄漏的罪魁禍?zhǔn)?/p>

索引:
1、valgrind 實戰(zhàn)
2赘风、”still reachable“ 是否關(guān)注
3夹囚、內(nèi)存泄漏的思考

1 內(nèi)存泄漏與 valgrind

1.1 什么是內(nèi)存泄漏?

內(nèi)存泄漏(Memory Leak)是指程序中已動態(tài)分配的堆內(nèi)存由于某種原因程序未釋放或無法釋放邀窃,造成系統(tǒng)內(nèi)存的浪費荸哟,導(dǎo)致程序運行速度減慢甚至系統(tǒng)崩潰等嚴(yán)重后果

內(nèi)存泄漏-百科
用程序員的話講:就是malloc 或者 new 了分配了內(nèi)存空間,由于某種原因沒有執(zhí)行free 或者delete釋放內(nèi)存空間瞬捕,導(dǎo)致系統(tǒng)內(nèi)存逐漸變少以至于無內(nèi)存可用

1.2 理論 && 實際

關(guān)于一些比較明確的內(nèi)存泄漏案例鞍历,我這里就簡單帶過,比如下面代碼

#include <iostream>
void leak(){
  char* p = new char[1024];
}
int main() {
  leak();
  return 0;
}

通過valgrind工具運行檢查肪虎,結(jié)果如下

==11562== HEAP SUMMARY:
==11562==     in use at exit: 73,728 bytes in 2 blocks
==11562==   total heap usage: 2 allocs, 0 frees, 73,728 bytes allocated
==11562==
==11562== 1,024 bytes in 1 blocks are definitely lost in loss record 1 of 2
==11562==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11562==    by 0x400717: leak() (in /home/a.out)
==11562==    by 0x400727: main (in /home/a.out)
==11562==
==11562== 72,704 bytes in 1 blocks are still reachable in loss record 2 of 2
==11562==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==11562==    by 0x4EC3EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==11562==    by 0x40106F9: call_init.part.0 (dl-init.c:72)
==11562==    by 0x401080A: call_init (dl-init.c:30)
==11562==    by 0x401080A: _dl_init (dl-init.c:120)
==11562==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==11562==
==11562== LEAK SUMMARY:
==11562==    definitely lost: 1,024 bytes in 1 blocks
==11562==    indirectly lost: 0 bytes in 0 blocks
==11562==      possibly lost: 0 bytes in 0 blocks
==11562==    still reachable: 72,704 bytes in 1 blocks
==11562==         suppressed: 0 bytes in 0 blocks

很明顯堰燎,leak()函數(shù)內(nèi)存泄露了,valgrind的檢測結(jié)果也明確指出:definitely lost: 1,024 bytes in 1 blocks

1.2.1 似而不是的內(nèi)存泄漏

? 那么我首先提一個問題:是否所有的malloc/new沒有對應(yīng)的free/delete笋轨,都是內(nèi)存泄漏,需要我們修復(fù)呢?

其實赊淑,這個問題并不難爵政,仔細看內(nèi)存泄漏的定義也可以得出答案,但是這里陶缺,我通過一段代碼钾挟,來給大家看看,加深理解

#include <iostream>
struct GlobalConf{
  int conf1;
  int conf2;
  int conf3;
};
GlobalConf* conf2;
int main() {
  conf2 = new GlobalConf();
  conf2->conf1 = 1;
  conf2->conf2 = 2;
  conf2->conf3 = 3;

  /*
   * 整個生命周期conf2都需要使用
   */
  while(1);
  return 0;
}

上面我們有一個全局配置饱岸,conf2指針掺出,程序初始化的時候,通過分配空間的方式分配內(nèi)存苫费,該程序一直運行不會結(jié)束汤锨,conf2雖然分配了內(nèi)存,但是其實只是在初始化的時候分配一次百框,且整個程序聲明周期一直在使用闲礼,所以雖然看起來分配了內(nèi)存未釋放,但是其實并不屬于內(nèi)存泄漏

1.2.2 引發(fā)血案的內(nèi)存泄漏

內(nèi)存泄漏之所以比較難查,一個原因就是不同于數(shù)組越界等其他的內(nèi)存非法訪問柬泽,存在很大的隱蔽性慎菲,而且有時候,valgrind提供的信息锨并,還需要結(jié)合我們的代碼露该,才能真正定位問題,下面第煮,我分析一個案例解幼,講述still reachable在我們分析問題時候的重要性,不可忽視空盼,特別是blocks比較多书幕,一定需要關(guān)注

#include <unistd.h>
#include <iostream>
#include <thread>
#include <vector>
class Buffer {
 public:
  Buffer(int size) : size_(size) { buf_ = new char[size]; }
  ~Buffer() {
    size_ = 0;
    delete[] buf_;
  }
 private:
  char* buf_;
  int size_;
};
std::vector<std::shared_ptr<Buffer>> vectors_;
std::vector<std::shared_ptr<Buffer>> vectors2_;
void t1_handler() {
  while (1) {
    auto item = std::make_shared<Buffer>(1024);
    vectors_.emplace_back(item);
    auto item2 = std::make_shared<Buffer>(1024);
    vectors2_.emplace_back(item2);
    std::cout << "insert" << std::endl;
    sleep(2);
  }
}
void t2_handler() {
  while (1) {
    if (!vectors_.empty()) {
      std::cout << "before --> size:" << vectors_.size() << std::endl;
      vectors_.clear();
      std::cout << "after --> size:" << vectors_.size() << std::endl;
      std::cout << "vector2 --> size:" << vectors2_.size() << std::endl;
      sleep(1);
    }
  }
}
int main() {
  std::thread t1(t1_handler);
  std::thread t2(t2_handler);
  t1.detach();
  t2.detach();

  while (1)
    ;
  return 0;
}

通過valgrind執(zhí)行程序的日志如下:

==18654== HEAP SUMMARY:
==18654==     in use at exit: 87,376 bytes in 33 blocks
==18654==   total heap usage: 60 allocs, 27 frees, 100,256 bytes allocated
==18654==

省略...

==18654== 12,288 bytes in 12 blocks are still reachable in loss record 10 of 11
==18654==    at 0x4C2E80F: operator new[](unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18654==    by 0x40170F: Buffer::Buffer(int) (in /home/a.out)
==18654==    by 0x4037F7: _ZN9__gnu_cxx13new_allocatorI6BufferE9constructIS1_IiEEEvPT_DpOT0_ (in /home/a.out)
==18654==    by 0x4034CD: _ZNSt16allocator_traitsISaI6BufferEE9constructIS0_IiEEEvRS1_PT_DpOT0_ (in /home/a.out)
==18654==    by 0x403123: std::_Sp_counted_ptr_inplace<Buffer, std::allocator<Buffer>, (__gnu_cxx::_Lock_policy)2>::_Sp_counted_ptr_inplace<int>(std::allocator<Buffer>, int&&) (in /home/a.out)
==18654==    by 0x402C86: std::__shared_count<(__gnu_cxx::_Lock_policy)2>::__shared_count<Buffer, std::allocator<Buffer>, int>(std::_Sp_make_shared_tag, Buffer*, std::allocator<Buffer> const&, int&&) (in /home/a.out)
==18654==    by 0x402915: std::__shared_ptr<Buffer, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<Buffer>, int>(std::_Sp_make_shared_tag, std::allocator<Buffer> const&, int&&) (in /home/a.out)
==18654==    by 0x402397: std::shared_ptr<Buffer>::shared_ptr<std::allocator<Buffer>, int>(std::_Sp_make_shared_tag, std::allocator<Buffer> const&, int&&) (in /home/a.out)
==18654==    by 0x401CB3: std::shared_ptr<Buffer> std::allocate_shared<Buffer, std::allocator<Buffer>, int>(std::allocator<Buffer> const&, int&&) (in /home/a.out)
==18654==    by 0x401833: _ZSt11make_sharedI6BufferIiEESt10shared_ptrIT_EDpOT0_ (in /home/a.out)
==18654==    by 0x4012AF: t1_handler() (in /home/a.out)
==18654==    by 0x404358: void std::_Bind_simple<void (*())()>::_M_invoke<>(std::_Index_tuple<>) (in /home/a.out)
==18654==
==18654== 72,704 bytes in 1 blocks are still reachable in loss record 11 of 11
==18654==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18654==    by 0x50E0EFF: ??? (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)
==18654==    by 0x40106F9: call_init.part.0 (dl-init.c:72)
==18654==    by 0x401080A: call_init (dl-init.c:30)
==18654==    by 0x401080A: _dl_init (dl-init.c:120)
==18654==    by 0x4000C69: ??? (in /lib/x86_64-linux-gnu/ld-2.23.so)
==18654==
==18654== LEAK SUMMARY:
==18654==    definitely lost: 0 bytes in 0 blocks
==18654==    indirectly lost: 0 bytes in 0 blocks
==18654==      possibly lost: 576 bytes in 2 blocks
==18654==    still reachable: 86,800 bytes in 31 blocks
==18654==         suppressed: 0 bytes in 0 blocks
==18654==

valgrind 是在程序結(jié)束的時候檢測,上面程序while運行不能結(jié)束揽趾,因此需要ctrl+c結(jié)束程序

上面still reachable顯示有12blocks泄漏了12M的數(shù)據(jù)台汇,位置是在t1_handler中分配的。
上面看起來可能一眼能看出來篱瞎,vectors2_沒被清理苟呐,那是因為這是一個簡單的測試demo,顯示中這種隱蔽的case可能就比較難查了


關(guān)于valgrind的經(jīng)驗總結(jié):

  1. definitely lost 需要全部清理掉
  2. still reachable 需要關(guān)注俐筋,但是需要case by case的分析牵素,特別是有多個blocks泄漏時,表明內(nèi)存泄漏位置被重復(fù)調(diào)用了澄者,這種一般會在長時間運行后由于無內(nèi)存可用導(dǎo)致系統(tǒng)崩潰
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笆呆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子粱挡,更是在濱河造成了極大的恐慌赠幕,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,546評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件询筏,死亡現(xiàn)場離奇詭異榕堰,居然都是意外死亡,警方通過查閱死者的電腦和手機嫌套,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,224評論 3 395
  • 文/潘曉璐 我一進店門逆屡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人踱讨,你說我怎么就攤上這事魏蔗。” “怎么了痹筛?”我有些...
    開封第一講書人閱讀 164,911評論 0 354
  • 文/不壞的土叔 我叫張陵沫勿,是天一觀的道長挨约。 經(jīng)常有香客問我,道長产雹,這世上最難降的妖魔是什么诫惭? 我笑而不...
    開封第一講書人閱讀 58,737評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮蔓挖,結(jié)果婚禮上夕土,老公的妹妹穿的比我還像新娘。我一直安慰自己瘟判,他們只是感情好怨绣,可當(dāng)我...
    茶點故事閱讀 67,753評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拷获,像睡著了一般篮撑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上匆瓜,一...
    開封第一講書人閱讀 51,598評論 1 305
  • 那天赢笨,我揣著相機與錄音,去河邊找鬼驮吱。 笑死茧妒,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的左冬。 我是一名探鬼主播桐筏,決...
    沈念sama閱讀 40,338評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拇砰!你這毒婦竟也來了梅忌?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,249評論 0 276
  • 序言:老撾萬榮一對情侶失蹤除破,失蹤者是張志新(化名)和其女友劉穎铸鹰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體皂岔,經(jīng)...
    沈念sama閱讀 45,696評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,888評論 3 336
  • 正文 我和宋清朗相戀三年展姐,在試婚紗的時候發(fā)現(xiàn)自己被綠了躁垛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,013評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡圾笨,死狀恐怖教馆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情擂达,我是刑警寧澤土铺,帶...
    沈念sama閱讀 35,731評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響悲敷,放射性物質(zhì)發(fā)生泄漏究恤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,348評論 3 330
  • 文/蒙蒙 一后德、第九天 我趴在偏房一處隱蔽的房頂上張望部宿。 院中可真熱鬧,春花似錦瓢湃、人聲如沸理张。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,929評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雾叭。三九已至,卻和暖如春落蝙,著一層夾襖步出監(jiān)牢的瞬間织狐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,048評論 1 270
  • 我被黑心中介騙來泰國打工掘殴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留赚瘦,地道東北人。 一個月前我還...
    沈念sama閱讀 48,203評論 3 370
  • 正文 我出身青樓奏寨,卻偏偏與公主長得像起意,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子病瞳,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,960評論 2 355

推薦閱讀更多精彩內(nèi)容