1. 程式人生 > >linux C記憶體洩露檢測實現及記憶體洩露檢測的一般方法

linux C記憶體洩露檢測實現及記憶體洩露檢測的一般方法

linux中,由於使用malloc或alloc而沒有free掉申請的記憶體,就會造成記憶體的洩露。通常,來講為了避免記憶體洩露的情況出現,一般要求,我們儘量的malloc之後,呼叫free。但是總會有忘記free的時候啊。一般可以有如下幾種方式來避免記憶體洩露:

1)  使用智慧指標,這個在C++中較為常見;

2)  使用記憶體池;

3)  自己封裝一層malloc/free等等。當申請記憶體時,將申請資訊放入到一個已分配記憶體資訊連結串列裡面。free時,刪除對應的資訊表的節點。在程式執行結束前,掃瞄該資訊表,若還存在資訊節點,那麼該節點記錄的就是洩露的記憶體資訊。若連結串列為空,那就是說沒有發生記憶體洩露;

4)使用檢測工具檢測記憶體洩露,進而修補程式,這樣的工具有比如Valgrind等等。

以下,給出一個按方式3實現的記憶體洩露檢測的實現版本,程式碼量本身不多。

leak_detector_c.h檔案

 #ifndef  LEAK_DETECTOR_C_H
  #define  LEAK_DETECTOR_C_H
  
  #define  FILE_NAME_LENGTH          256
  #define  OUTPUT_FILE               "/home/leak_info.txt" /*this file should be created first!*/
  #define  malloc(size)               xmalloc (size, __FILE__, __LINE__)
  #define  calloc(elements, size)     xcalloc (elements, size, __FILE__, __LINE__)
  #define  free(mem_ref)              xfree(mem_ref)
  
  struct _MEM_INFO
  {
      void            *address;
      unsigned int    size;
      char            file_name[FILE_NAME_LENGTH];
      unsigned int    line;
  };
  typedef struct _MEM_INFO MEM_INFO;
  
  struct _MEM_LEAK {
      MEM_INFO mem_info;
      struct _MEM_LEAK * next;
  };
  typedef struct _MEM_LEAK MEM_LEAK;
  
  void add(MEM_INFO alloc_info);
  void erase(unsigned pos);
  void clear(void);
  
  void * xmalloc(unsigned int size, const char * file, unsigned int line);
  void * xcalloc(unsigned int elements, unsigned int size, const char * file, unsigned int line);
  void xfree(void * mem_ref);
  
  void add_mem_info (void * mem_ref, unsigned int size,  const char * file, unsigned int line);
  void remove_mem_info (void * mem_ref);
  void report_mem_leak(void);
  
 #endif

leak_detector_c.c檔案

#include    <stdio.h>
#include    <malloc.h>
#include    <string.h>
#include    "leak_detector_c.h"
 
#undef      malloc
#undef      calloc
#undef      free
  
  
static MEM_LEAK * ptr_start = NULL;
static MEM_LEAK * ptr_next =  NULL;

/*
* adds allocated memory info. into the list
*
*/
void add(MEM_INFO alloc_info)
{

     MEM_LEAK * mem_leak_info = NULL;
     mem_leak_info = (MEM_LEAK *) malloc (sizeof(MEM_LEAK));
     mem_leak_info->mem_info.address = alloc_info.address;
     mem_leak_info->mem_info.size = alloc_info.size;
     strcpy(mem_leak_info->mem_info.file_name, alloc_info.file_name); 
     mem_leak_info->mem_info.line = alloc_info.line;
     mem_leak_info->next = NULL;
 
    if (ptr_start == NULL)  
    {
        ptr_start = mem_leak_info;
        ptr_next = ptr_start;
    }
    else {
        ptr_next->next = mem_leak_info;
        ptr_next = ptr_next->next;               
    }

}
/*
  * erases memory info. from the list
  *
*/
void erase(unsigned pos)
{
 
     unsigned index = 0;
     MEM_LEAK * alloc_info, * temp;
      
      if(pos == 0)
      {
          MEM_LEAK * temp = ptr_start;
          ptr_start = ptr_start->next;
          free(temp);
      }
      else 
      {
          for(index = 0, alloc_info = ptr_start; index < pos; 
              alloc_info = alloc_info->next, ++index)
          {
              if(pos == index + 1)
              {
                  temp = alloc_info->next;
                  alloc_info->next =  temp->next;
                 if(temp->next==NULL)
                  {
                    ptr_next = alloc_info;
                  }
                  free(temp);
                  break;
              }
          }
      }
  }
  
  /*
   * deletes all the elements from the list
   */
  void clear()
  {
      MEM_LEAK * temp = ptr_start;
      MEM_LEAK * alloc_info = ptr_start;
  
      while(alloc_info != NULL) 
      {
          alloc_info = alloc_info->next;
          free(temp);
          temp = alloc_info;
      }
      ptr_start=NULL;
  }
  
  /*
   * replacement of malloc
   */
  void * xmalloc (unsigned int size, const char * file, unsigned int line)
  {
      void * ptr = malloc (size);
      if (ptr != NULL) 
      {
          add_mem_info(ptr, size, file, line);
      }
      return ptr;
  }
  
  /*
   * replacement of calloc
   */
  void * xcalloc (unsigned int elements, unsigned int size, const char * file, unsigned int line)
  {
      unsigned total_size;
      void * ptr = calloc(elements , size);
      if(ptr != NULL)
      {
          total_size = elements * size;
          add_mem_info (ptr, total_size, file, line);
      }
      return ptr;
  }
  
  
  /*
   * replacement of free
   */
  void xfree(void * mem_ref)
  {
      remove_mem_info(mem_ref);
      free(mem_ref);
  }
  
/*
* gets the allocated memory info and adds it to a list
*
*/
  void add_mem_info (void * mem_ref, unsigned int size,  const char * file, unsigned int line)
  {
     MEM_INFO mem_alloc_info;
 
     /* fill up the structure with all info */
     memset( &mem_alloc_info, 0, sizeof ( mem_alloc_info ) );
      mem_alloc_info.address  = mem_ref;
      mem_alloc_info.size = size;
      strncpy(mem_alloc_info.file_name, file, FILE_NAME_LENGTH);
     mem_alloc_info.line = line;
     
     /* add the above info to a list */
      add(mem_alloc_info);
 }
  
  /*
   * if the allocated memory info is part of the list, removes it
  *
  */
 void remove_mem_info (void * mem_ref)
 {
    unsigned short index;
     MEM_LEAK  * leak_info = ptr_start;
 
     /* check if allocate memory is in our list */
    for(index = 0; leak_info != NULL; ++index, leak_info = leak_info->next)
    {
        if ( leak_info->mem_info.address == mem_ref )
        {
             erase ( index );
             break;
        }
    }
}

/*
  * writes all info of the unallocated memory into a file
  */
 void report_mem_leak(void)
{
    unsigned short index;
    MEM_LEAK * leak_info;


    FILE * fp_write = fopen (OUTPUT_FILE, "w+");
    char info[1024];


     if(fp_write != NULL)
     {
         sprintf(info, "%s\r\n", "Memory Leak Summary");
         fwrite(info, (strlen(info)) , 1, fp_write);
         sprintf(info, "%s\r\n", "-----------------------------------");
         fwrite(info, (strlen(info)) , 1, fp_write);


        for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next)
        {
             sprintf(info, "address : %d\r\n", leak_info->mem_info.address);
             fwrite(info, (strlen(info)) , 1, fp_write);
             sprintf(info, "size    : %d bytes\r\n", leak_info->mem_info.size);
             fwrite(info, (strlen(info)) , 1, fp_write);
             sprintf(info, "file    : %s\r\n", leak_info->mem_info.file_name);
             fwrite(info, (strlen(info)) , 1, fp_write);
             sprintf(info, "line    : %d\r\n", leak_info->mem_info.line);
             fwrite(info, (strlen(info)) , 1, fp_write);
             sprintf(info, "%s\r\n", "-----------------------------------");
             fwrite(info, (strlen(info)) , 1, fp_write);


             printf("addr=%d,size=%d,file=%s,line=%d\n"
                ,leak_info->mem_info.address
                ,leak_info->mem_info.size
                ,leak_info->mem_info.file_name
                ,leak_info->mem_info.line);
        }


        fclose(fp_write);
      }
      else
      {


      }
      /*clear();*/
}

使用時,只需將以上兩個檔案放入到你的工程目錄下,並在主函式裡面呼叫report_mem_leak就OK了。

測試test.c檔案

#include    "leak_detector_c.h"
int main()
{
    char * ptr1 = (char *)malloc(10);
    int * ptr2 = (int *)calloc(10, sizeof(int));
    float * ptr3 = (float *) calloc(15, sizeof(float));
    free(ptr2);
atexit(report_mem_leak);
return 0;
}

編譯test.c並執行,可以在/home/leak_info.txt檔案中,看到兩條記憶體洩露記錄。

總的來說,這樣的檢測方式還是有一定效果的,但是同時記憶體資訊連結串列節點也使用了malloc來申請記憶體,這就造成了一定的額外的記憶體開銷。

我覺得要避免記憶體洩露,首先是自身要養成良好的程式設計習慣,然後是使用相關的方法和工具對程式碼進行檢測,最後是要閱讀程式碼了。工具也並不能檢測出所有的記憶體洩露,在這個時候只能靠自己去閱讀程式碼分析程式碼了,人畢竟比機器靈活嘛!


./autogen.sh


./configure --host=powerpc-linux CC=powerpc-linux-gnu-gcc CPP=powerpc-linux-gnu-cpp CXX=powerpc-linux-gnu-g++ --prefix=/mnt/nand


make


make install

 ./valgrind --tool=memcheck --vgdb=no ./memleak 

注意:

1. --prefix=/mnt/nand 指定的目錄要與開發板上放置的目錄一致

2. 引數必須 --vgdb=no 否則會如下錯誤(可能因為我嵌入式linux 沒有整合gdb)

[email protected]:/mnt/nand/bin# ./valgrind --tool=memcheck ./memleak 
==1300== Memcheck, a memory error detector
==1300== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1300== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==1300== Command: ./memleak
==1300== 
==1300== error 2 No such file or directory
==1300== mknod /tmp/vgdb-pipe-from-vgdb-to-1300-by-root-on-/bin/hostname
==1300== valgrind: fatal error: vgdb FIFOs cannot be created.


相關推薦

linux C記憶體洩露檢測實現記憶體洩露檢測一般方法

linux中,由於使用malloc或alloc而沒有free掉申請的記憶體,就會造成記憶體的洩露。通常,來講為了避免記憶體洩露的情況出現,一般要求,我們儘量的malloc之後,呼叫free。但是總會有忘記free的時候啊。一般可以有如下幾種方式來避免記憶體洩露: 1) 

c語言堆與棧記憶體分配

原文:http://www.cnblogs.com/TonyEwsn/archive/2010/01/29/1659496.html 格式和部分內容稍作修改。 在計算機領域,堆疊是一個不容忽視的概念,我們編寫的C語言程式基本上都要用到。但對於很多的初學著來說,堆疊是

Linux c++,用訊號量實現消費者生產者佇列(程式碼可直接通過編譯)

//用訊號量實現的一個消費者生產者佇列, #include <iostream> #include <pthread.h> #include <semaphore.h> #include <errno.h> #include <queue>

Linux C Socket UDP程式設計介紹例項

1、UDP網路程式設計主要流程 UDP協議的程式設計框架,客戶端和伺服器之間的差別在於伺服器必須使用bind()函式來繫結偵聽的本地UDP埠,而客戶端則可以不進行繫結,直接傳送到伺服器地址的某個埠地址。框圖如圖1.3所示 UDP協議的伺服器端流程 伺服器流程主要分為下述6個

nginx原始碼分析—記憶體池結構ngx_pool_t記憶體管理

本部落格(http://blog.csdn.net/livelylittlefish)貼出作者(阿波)相關研究、學習內容所做的筆記,歡迎廣大朋友指正!Content0. 序1. 記憶體池結構1.1 ngx_pool_t結構1.2 其他相關結構1.3 ngx_pool_t的邏輯

C++多型的實現原理詳細解析

C++的多型性用一句話概括就是: 在基類的函式前加上virtual關鍵字,在派生類中重寫該函式,執行時將會根據物件的實際型別來呼叫相應的函式。 如果物件型別是派生類,就呼叫派生類的函式;如果物件型別是基類,就呼叫基類的函式,此為多型的表現; 在看看以下幾點: 1. 用vir

Linux c==使用父子程序實現TCP通訊

include include include include include include include include define MY_PORT 3333 int main(int argc ,char **argv) { int liste

獲取Linux命令幫助信息,man手冊的使用方法

Linux man 幫助 Linux中命令的類型 Linux 中命令類型分為內部命令和外部命令。 使用type命令判斷內部和外部命令,示例如下: [root@centos7 ~]# type cd cd is a shell builtin #

linux伺服器中不支援soapbcmul函式的結局方法

新的程式裡用了webserice介面,部到伺服器,先是提示:bcmul() 函式不可用,網上搜索一番,得知這是php的高精度函式,需要在編譯php的時候加入此模組,於是在編譯腳本里增添 “–enable-bcmath” 後重新編譯、重啟apache然後此錯誤解決; 然後有遇到報錯“Class ‘SoapCl

Linux C 程式執行 shell 命令並獲取返回結果的方法

據說有統計資料表明,程式碼的缺陷率是一定的,與所使用的語言無關。Linux提供了很多的實用工具和指令碼,在程式中呼叫工具和指令碼,無疑可以簡化程式,從而降低程式碼的缺陷數目。Linux shell 指令碼也是一個強大的工具,我們可以根據需要編制指

linux centos7 下Nginx伺服器實現URL重寫去掉index.php方法,跟Apache還是不一樣的。

想要的URL :http://localhost/Admin/ 而自己的是http://localhost/index.php/Admin/ 方法: 在nginx配置檔案nginx.conf中新增: location / {   if ( !e $request_filename )

C#中呼叫SAPI實現語音識別的2種方法

通過微軟的SAPI,不僅僅可以實現語音合成TTS,同樣可以實現語音識別SR。下面我們就介紹並貼出相關程式碼。主要有兩種方式: 1、使用COM元件技術,不管是C++,C#,Delphi都能玩的轉,開發出來的東西在XP和WIN7都能跑。(注意要引入系統元件SpeechLib,XP要安裝識別引擎) 2、

Linux系統date命令的引數獲取時間戳的方法

date指令相關用法示例 date 用法: date [OPTION]... [+FORMAT] date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]] 直接輸入date date 指定格式顯示時間: date +%Y

linux mac svn ignore忽略檔案資料夾最好用方法 之*妙用

一、忽略單個檔案 忽略檔案必須要進入到檔案的當前目錄,比如要忽略這個database.php配置檔案。 先進入application資料夾,輸入兩條命令語法如下: svn propset svn:ignore '要忽略的檔名' ./ svn ci -m 'ignore

Delphi 中自定義異常異常處理的一般方法

   delphi中異常定義如下: TCustomException   =   class(Exception)     private     public         constructor   Create(const   Msg:   string );

Linux C 程式設計記憶體洩露檢測工具(二):memwatch

Memwatch簡介 在三種檢測工具當中,設定最簡單的算是memwatch,和dmalloc一樣,它能檢測未釋放的記憶體、同一段記憶體被釋放多次、位址存取錯誤及不當使用未分配之記憶體區域。請往http://www.linkdata.se/sourcecode.html下載最

linux c 程式碼測試之記憶體越界記憶體洩露

 記憶體越界是我們軟體開發中經常遇到的一個問題。不經意間的複製常常導致很嚴重的後果。經常使用memset、memmove、strcpy、strncpy、strcat、sprintf的朋友肯定對此印象深刻,下面就是我個人在開發中實際遇到的一個開發問題,頗具典型。 #

C語言動態記憶體分配:(一)malloc/free的實現malloc實際分配/釋放的記憶體

一、malloc/free概述 malloc是在C語言中用於在程式執行時在堆中進行動態記憶體分配的庫函式。free是進行記憶體釋放的庫函式。 1、函式原型 #include <stdlib.h> void *malloc( size_t size

使用ReferenceQueue實現對ClassLoader垃圾回收過程的觀察、以及由此引發的ClassLoader記憶體洩露的場景排查過程

1 使用Reference/ReferenceQueue觀察Class和ClassLoader的解除安裝在java中,存在著強引用(=),軟引用(SoftReference),弱引用(WeakReference),虛引用(PhantomReference)這4種引用型別。如果

java中遠端監控Linux主機CPU記憶體程式碼實現

對於遠端監控Linux主機系統CPU,記憶體使用情況,以前也使用了top等命令,但是最後還是覺得使用vmstat比較好. 執行top命令獲得系統CPU使用情況有兩個缺點, 第一執行top命令,執行的shell語句相對複雜. 用top命令獲得CPU使用情況的shell語句 t