日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
我擼了個(gè)內(nèi)存泄漏檢測(cè)工具,只用了兩招

本文轉(zhuǎn)載自微信公眾號(hào)「程序喵大人」,作者程序喵大人。轉(zhuǎn)載本文請(qǐng)聯(lián)系程序喵大人公眾號(hào)。

站在用戶的角度思考問(wèn)題,與客戶深入溝通,找到施秉網(wǎng)站設(shè)計(jì)與施秉網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、主機(jī)域名、網(wǎng)頁(yè)空間、企業(yè)郵箱。業(yè)務(wù)覆蓋施秉地區(qū)。

大家看我寫了這么長(zhǎng)時(shí)間C++文章,殊不知我在工作中已經(jīng)一年多沒(méi)有用過(guò)C++了,最近做一個(gè)新項(xiàng)目,終于又回到C++的懷抱了,有點(diǎn)激動(dòng),也有點(diǎn)不適應(yīng)。

不管使用什么語(yǔ)言,一定要處理好內(nèi)存問(wèn)題,要有檢測(cè)內(nèi)存問(wèn)題的方法論,于是擼了個(gè)檢測(cè)是否有泄漏的小工具,這里分享一波。

先貼個(gè)效果圖:

實(shí)現(xiàn)方法

眾所周知C++中申請(qǐng)和釋放內(nèi)存使用的是new和delete關(guān)鍵字:

 
 
 
 
  1. void func() { 
  2.     A* a = new A(); 
  3.     delete a; 
  4.     A* b = new int[4]; 
  5.     delete[] b; 

再明確下需求:如果程序中存在內(nèi)存泄漏,我們的目的是找到這些內(nèi)存是在哪里分配的,如果能夠具體對(duì)應(yīng)到代碼中哪一個(gè)文件的那一行代碼最好。好了需求明確了,開(kāi)始實(shí)現(xiàn)。

內(nèi)存在哪里釋放的我們沒(méi)必要監(jiān)測(cè),只需要檢測(cè)出內(nèi)存是在哪里申請(qǐng)的即可,如何檢測(cè)呢?

整體思路很簡(jiǎn)單:在申請(qǐng)內(nèi)存時(shí)記錄下該內(nèi)存的地址和在代碼中申請(qǐng)內(nèi)存的位置,在內(nèi)存銷毀時(shí)刪除該地址對(duì)應(yīng)的記錄,程序最后統(tǒng)計(jì)下還有哪條記錄沒(méi)有被刪除,如果還有沒(méi)被刪除的記錄就代表有內(nèi)存泄漏。

很多人應(yīng)該都知道new關(guān)鍵字更底層是通過(guò)operator new來(lái)申請(qǐng)內(nèi)存的:

 
 
 
 
  1. void* operator new(std::size_t sz) 

也就是正常情況下C++都是通過(guò)operator new(std::size_t sz)來(lái)申請(qǐng)內(nèi)存,而這個(gè)操作符我們可以重載:

 
 
 
 
  1. void* operator new(std::size_t size, const char* file, int line); 
  2. void* operator new[](std::size_t size, const char* file, int line); 

tip:new和new[]的區(qū)別我就不具體介紹了,太基礎(chǔ)。

如果能讓程序申請(qǐng)內(nèi)存時(shí)調(diào)用重載的這個(gè)函數(shù),就可以記錄下內(nèi)存申請(qǐng)的具體位置啦。

怎么能夠讓底層程序申請(qǐng)內(nèi)存時(shí)調(diào)用重載的這個(gè)函數(shù)呢?這里可以對(duì)new使用宏定義:

 
 
 
 
  1. #define new new (__FILE__, __LINE__) 

有了這個(gè)宏定義后,在new A的時(shí)候底層就會(huì)自動(dòng)調(diào)用operator new(std::size_t size, const char* file, int line)函數(shù),至此達(dá)到了我們記錄內(nèi)存申請(qǐng)位置的目的。

這里有兩個(gè)問(wèn)題:

  1. 在哪里記錄內(nèi)存申請(qǐng)的位置等信息呢?如果在operator new內(nèi)部又申請(qǐng)了一塊內(nèi)存,用于記錄位置,那新申請(qǐng)的這塊內(nèi)存需要記錄不?這豈不是遞歸調(diào)用了?
  2. 只有在new宏定義包裹范圍內(nèi)申請(qǐng)了內(nèi)存才會(huì)被記錄,然而某些第三方庫(kù)或者某些地方?jīng)]有被new宏定義包裹,可能就無(wú)法被監(jiān)測(cè)是否申請(qǐng)了內(nèi)存吧?

下面逐個(gè)擊破:

哪里存儲(chǔ)具體信息?

我們肯定不能讓它遞歸調(diào)用啊,那這些信息存儲(chǔ)在哪里呢?這里可以在每次申請(qǐng)內(nèi)存時(shí),一次性申請(qǐng)一塊稍微大點(diǎn)的內(nèi)存,具體信息存儲(chǔ)在多余的那塊內(nèi)存里,像這樣:

 
 
 
 
  1. static void* alloc_mem(std::size_t size, const char* file, int line, bool is_array) { 
  2.     assert(line >= 0); 
  3.  
  4.     std::size_t s = size + ALIGNED_LIST_ITEM_SIZE; 
  5.     new_ptr_list_t* ptr = (new_ptr_list_t*)malloc(s); 
  6.     if (ptr == nullptr) { 
  7.         std::unique_lock lock(new_output_lock); 
  8.         printf("Out of memory when allocating %lu bytes\n", (unsigned long)size); 
  9.         abort(); 
  10.     } 
  11.     void* usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; 
  12.  
  13.     if (line) { 
  14.         strncpy(ptr->file, file, _DEBUG_NEW_FILENAME_LEN - 1)[_DEBUG_NEW_FILENAME_LEN - 1] = '\0'; 
  15.     } else { 
  16.         ptr->addr = (void*)file; 
  17.     } 
  18.  
  19.     ptr->line = line; 
  20.     ptr->is_array = is_array; 
  21.     ptr->size = size; 
  22.     ptr->magic = DEBUG_NEW_MAGIC; 
  23.     { 
  24.         std::unique_lock lock(new_ptr_lock); 
  25.         ptr->prev = new_ptr_list.prev; 
  26.         ptr->next = &new_ptr_list; 
  27.         new_ptr_list.prev->next = ptr; 
  28.         new_ptr_list.prev = ptr; 
  29.     } 
  30.     total_mem_alloc += size; 
  31.     return usr_ptr; 

new_ptr_list_t結(jié)構(gòu)體定義如下:

 
 
 
 
  1. struct new_ptr_list_t { 
  2.     new_ptr_list_t* next; 
  3.     new_ptr_list_t* prev; 
  4.     std::size_t size; 
  5.     union { 
  6.         char file[200]; 
  7.  
  8.         void* addr; 
  9.     }; 
  10.     unsigned line; 
  11. }; 

沒(méi)有被new宏包裹的地方可以檢測(cè)的到嗎?

沒(méi)有被new宏包裹的地方是會(huì)調(diào)用operator new(std::size_t sz)函數(shù)來(lái)申請(qǐng)內(nèi)存的。這里operator new函數(shù)不只可以重載,還可以重新定義它的實(shí)現(xiàn),而且不會(huì)報(bào)multi definition的錯(cuò)誤哦。因?yàn)樗且粋€(gè)weak symbol,有關(guān)strong symbol和weak symbol的知識(shí)點(diǎn)可以看我之前的一篇文章:《談?wù)劤绦蜴溄蛹胺侄文切┦隆?/p>

既然可以重定義,那就可以這樣:

 
 
 
 
  1. void* operator new(std::size_t size) {  
  2.     return operator new(size, nullptr, 0);  

這樣有個(gè)缺點(diǎn),就是不能記錄內(nèi)存申請(qǐng)的具體代碼位置,只能記錄下來(lái)是否申請(qǐng)過(guò)內(nèi)存,不過(guò)這也挺好,怎么也比沒(méi)有任何感知強(qiáng)的多。

其實(shí)這里不是沒(méi)有辦法,盡管沒(méi)有了new宏,獲取不到具體申請(qǐng)內(nèi)存的代碼位置,但是可以獲取到調(diào)用棧信息,把調(diào)用棧信息存儲(chǔ)起來(lái),還是可以定位大體位置。關(guān)于如何獲取調(diào)用棧信息,大家可以研究下libunwind庫(kù)看看。

釋放內(nèi)存時(shí)怎么辦?

這里需要重定義operator delete(void* ptr)函數(shù):

 
 
 
 
  1. void operator delete(void* ptr) noexcept {  
  2.     free_pointer(ptr, nullptr, false);  

free_pointer函數(shù)的大體思路就是在鏈表中找到要對(duì)應(yīng)節(jié)點(diǎn),刪除掉,具體定義如下:

 
 
 
 
  1. static void free_pointer(void* usr_ptr, void* addr, bool is_array) { 
  2.     if (usr_ptr == nullptr) { 
  3.         return; 
  4.     } 
  5.     new_ptr_list_t* ptr = (new_ptr_list_t*)((char*)usr_ptr - ALIGNED_LIST_ITEM_SIZE); 
  6.     { 
  7.         std::unique_lock lock(new_ptr_lock); 
  8.         total_mem_alloc -= ptr->size; 
  9.         ptr->magic = 0; 
  10.         ptr->prev->next = ptr->next; 
  11.         ptr->next->prev = ptr->prev; 
  12.     } 
  13.     free(ptr); 

如何檢測(cè)是否有內(nèi)存泄漏?

遍歷鏈表即可,每次new時(shí)候會(huì)把這段內(nèi)存插入鏈表,delete時(shí)候會(huì)把這段內(nèi)存從鏈表中移出,如果程序最后鏈表長(zhǎng)度不為0,即為有內(nèi)存泄漏,代碼如下:

 
 
 
 
  1. int checkLeaks() { 
  2.     int leak_cnt = 0; 
  3.     int whitelisted_leak_cnt = 0; 
  4.     new_ptr_list_t* ptr = new_ptr_list.next; 
  5.  
  6.     while (ptr != &new_ptr_list) { 
  7.         const char* const usr_ptr = (char*)ptr + ALIGNED_LIST_ITEM_SIZE; 
  8.         printf("Leaked object at %p (size %lu, ", usr_ptr, (unsigned long)ptr->size); 
  9.         if (ptr->line != 0) { 
  10.             print_position(ptr->file, ptr->line); 
  11.         } else { 
  12.             print_position(ptr->addr, ptr->line); 
  13.         } 
  14.         printf(")\n"); 
  15.         ptr = ptr->next; 
  16.         ++leak_cnt; 
  17.     } 
  18.     return leak_cnt; 

ps:關(guān)于可以重定義operator new這個(gè)操作,我也是最近看到別人代碼后才發(fā)現(xiàn),于是參考別人代碼小擼了個(gè)代碼檢測(cè)工具,希望大家有所收獲!


網(wǎng)頁(yè)名稱:我擼了個(gè)內(nèi)存泄漏檢測(cè)工具,只用了兩招
轉(zhuǎn)載來(lái)源:http://www.dlmjj.cn/article/ccooied.html