偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

我擼了個內(nèi)存泄漏檢測工具,只用了兩招

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

 [[389657]]

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

大家看我寫了這么長時間C++文章,殊不知我在工作中已經(jīng)一年多沒有用過C++了,最近做一個新項目,終于又回到C++的懷抱了,有點激動,也有點不適應(yīng)。

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

先貼個效果圖:

實現(xiàn)方法

眾所周知C++中申請和釋放內(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)存是在哪里分配的,如果能夠具體對應(yīng)到代碼中哪一個文件的那一行代碼最好。好了需求明確了,開始實現(xiàn)。

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

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

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

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

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

  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ǔ)。

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

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

  1. #define new new (__FILE__, __LINE__) 

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

這里有兩個問題:

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

下面逐個擊破:

哪里存儲具體信息?

我們肯定不能讓它遞歸調(diào)用啊,那這些信息存儲在哪里呢?這里可以在每次申請內(nèi)存時,一次性申請一塊稍微大點的內(nèi)存,具體信息存儲在多余的那塊內(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<std::mutex> 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<std::mutex> 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. }; 

沒有被new宏包裹的地方可以檢測的到嗎?

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

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

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

這樣有個缺點,就是不能記錄內(nèi)存申請的具體代碼位置,只能記錄下來是否申請過內(nèi)存,不過這也挺好,怎么也比沒有任何感知強的多。

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

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

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

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

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

  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<std::mutex> 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); 

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

遍歷鏈表即可,每次new時候會把這段內(nèi)存插入鏈表,delete時候會把這段內(nèi)存從鏈表中移出,如果程序最后鏈表長度不為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這個操作,我也是最近看到別人代碼后才發(fā)現(xiàn),于是參考別人代碼小擼了個代碼檢測工具,希望大家有所收獲!

 

責(zé)任編輯:武曉燕 來源: 程序喵大人
相關(guān)推薦

2021-11-05 08:29:13

數(shù)據(jù)校驗Spring

2017-05-24 17:25:44

2025-06-03 17:37:49

模型訓(xùn)練AI

2025-04-08 00:33:00

數(shù)據(jù)校驗Bean

2024-07-03 11:28:15

2019-09-09 11:35:21

GitHub工具瀏覽

2013-08-02 09:52:14

AndroidApp內(nèi)存泄漏

2021-12-23 11:10:38

稅收大數(shù)據(jù)大數(shù)據(jù)稅收

2017-09-07 16:52:23

2020-11-04 07:56:19

工具Linux 翻譯

2015-04-17 10:35:51

c++c++程序內(nèi)存泄漏檢測代碼

2020-03-06 08:15:54

新人技術(shù)主管陳琦

2016-09-12 16:01:28

Android內(nèi)存泄露內(nèi)存管理

2011-08-15 10:16:55

內(nèi)存泄露

2009-02-01 09:42:00

2023-07-17 07:06:51

電腦管家C盤文件

2024-04-19 15:55:01

系統(tǒng)設(shè)計繪圖工具

2009-11-20 10:55:13

Oracle數(shù)據(jù)比較

2018-05-23 16:56:40

戴爾

2024-12-06 11:38:12

點贊
收藏

51CTO技術(shù)棧公眾號