支持原創(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é):
- definitely lost 需要全部清理掉
- still reachable 需要關(guān)注俐筋,但是需要case by case的分析牵素,特別是有多個blocks泄漏時,表明內(nèi)存泄漏位置被重復(fù)調(diào)用了澄者,這種一般會在長時間運行后由于無內(nèi)存可用導(dǎo)致系統(tǒng)崩潰