zoukankan      html  css  js  c++  java
  • Hello, Memory Leak

    已经有3年没有认认真真写过C/C++程序。即使是写,也是写些小程序。

    因为项目的需要,与C/C++再续前缘。令人崩溃的是,我写的程序居然出现内存泄漏,直接吐血。以前,也有一次出现过内存泄漏问题,记得当时写的是一个monitor工具,一直运行在机器上,有意思的是每次执行泄漏4个字节。原因后来找到了,都是CString惹的祸啊。

    这次的情况和上次不一样,没那么简单,一直用到其他库,二是因为自己对这门语言开始生疏了,造成了很大的问题。今天一口气将其解决掉,经验分享如下。

    如何知道内存泄漏?

    我是用VS.NET 2003开发的,该工具挺好的,当我调试时然后关闭应用程序,在output窗口就会显示,例如,

    Detected memory leaks! 
    Dumping objects 
    -> 
    {
    10381} normal block at 0x01C76F404 bytes long
    Data: 
    <  t > EC 9C 74 00 
    {
    9443} normal block at 0x01C37D684 bytes long
    Data: 
    <  t > EC 9C 74 00 
    Object dump complete.

     说实话,上面提示只能告诉你内存泄漏存在,而且泄漏多少,但是没有告诉你在哪里泄漏。还好,一个开源的小工具能够帮助我们定位到具体位置。这个工具就是Visual Leak Detector , 至于如何使用,网上文章特多,在这里我还是简要说明一下。

    1. 将include文件放在系统的include下,如果使用VS,那就放在VS的include下。
    2. 将lib文件放系统的lib下,如果使用VS,就放在VS的lib下。
    3. 在需要的源文件引用一下就可以了,例如#include<vld.h>
    4. 将dll放在执行文件的目录那里。

    debug应用程序,然后关闭,在output下会出现:

    Code

     上面是最简单的方法有个缺点,要是我不想讲include等文件放在系统目录下,而想将其跟我的工程一起放怎么办?很简单,将include,lib等文件拷到自己的project下,如果使用VS,请在设置里将include,lib将这些目录加上,然后再lib的那里加上vld.lib即可。不同的IDE不一样,也许不用加上。

    当然还有很多更好的工具,不过VLD已经够用了。

    如何避免?

    避免内存泄漏的方法还是在于防范。

    我觉得最根本的原理就是请求内存,然后释放内存。往往我们就是只请求不释放。

    原则可以如下:

    1. new一个,一定要delete
    2. malloc一个,一定要free
    3. 有时候并没有new/malloc,但是还是请求了资源,这个时候也要free。
    4. 不要返回一个对象或者一个对象的指针,代替的是在参数里进行传址。
    5. 如果你非得返回一个对象或指针,那引用该函数的人一定有义务要删除。

    1、2不多说了,太简单,举个很有意思的例子。CString我们都熟悉,但是用好还真不容易,new了一个一定要删除,有时候会调用GetBuffer,事后是否需要ReleaseBuffer呢?

    说第3点。先举个例子:

    char* val = XMLString::transcode(subNode->getFirstChild()->getNodeValue()); 
    //do your thing here 
      XMLString::release(&val);

     上面val其实分配了内存,所以要在下面释放,XML::release就是干这个事情的。也许我这么说很明白,按时真到了关键时刻,可能就忘了,也许有这样的写法:

    std::string msg(XMLString::transcode(subNode->getFirstChild()->getNodeValue()));

    说不定我们写完了以为就没事了,但是事情往往就出现在这里。

    第4点,很多人都会犯,包括我自己。

    先看这样如下代码:

    Class A 

      
    public
      A(); 
      
    ~A() 

    class B 

    public
    B(); 
    ~B(); 

    A f1(){
    return new A();}; 
    A
    * f2(){A* a = new A(); return a;}; 

    B b; 
    A a1 
    = b.f1() 
    A
    * a2 = b.f2();

     上面的代码就是模拟第4点写的。第一看没问题,这在C#,Java里,这种思路在普遍不过了,但是在C/C++这里需要注意,返回一个临时对象是不安全的,即使访问指针,也是不安全的。如果要变的安全,需要费劲,而且你必须遵照第5点,手动删除它。

    什么是安全的使用方法?我们可以改写一下:

    Class A 

      
    public
      A(); 
      
    ~A() 

    class B 

    public
    B(); 
    ~B(); 

    int f1(A* a); 

    B b; 
    A
    * a1 =new A(); 
    b.f2(a1);

     这样做就安全许多。因为f1将指会存入a1指向的地址那里。

    其他

    为了避免内存泄漏,有个比较好的方法就是RAII(Resource Acquisition Is Initialization),一般用在类里,思想就是在析构函数里将资源释放掉,即使在使用的过程中忘了删除,最后对象销毁时也会释放的。举个例子:

    #include  
    #include  
    class file 

    public
        file (
    const char* filename) 
            : file_(std::fopen(filename, 
    "w+")) 
        { 
            
    if (!file_) 
                
    throw std::runtime_error("file open failure"); 
        } 
        
    ~file() 
        { 
            
    if (0 != std::fclose(file_)) // failed to flush latest changes? 
            { 
                
    // handle it 
            } 
        } 
        
    void write (const char* str) 
        { 
            
    if (EOF == std::fputs(str, file_)) 
                
    throw std::runtime_error("file write failure"); 
        } 
    private
        std::FILE
    * file_; 
        
    // prevent copying and assignment; not implemented 
        file (const file &); 
        file 
    & operator= (const file &); 
    };

     上面~file()里,也会把file_干掉的,即使字啊write里没有释放。

    程序员是很懒的,所以有时候不会主动释放掉,更不用说粗心大意的人了,即使是谨慎的人,也不会时时刻刻去关注。即使是,后期维护人员难道知道处处到要释放吗?不能保证啊。

    上面是方法之一,接下来就是只能指针,auto_ptr,当这个指针销毁时,其对象也销毁。看看下面的代码:

    using namespace std; 

    class MyClass { 
    public
      MyClass() {} 
    // nothing 
      ~MyClass() {} // nothing 
      void myFunc() {} // nothing 
    }; 

    int main() { 
      auto_ptr
    <MyClass> ptr1(new MyClass), ptr2; 

      ptr2 
    = ptr1; 
      ptr2
    ->myFunc(); 

      MyClass
    * ptr = ptr2.get(); 

      ptr
    ->myFunc(); 

      
    return 0
    }

     如果ptr1销毁后,那么在new MyClass()也会销毁。

    关于智能指针我不多说了,毕竟我也不是特别精通啊。

    暂时就写这么多,以后在更新吧。

  • 相关阅读:
    多线程中static对象
    DPDK l2fwd 浅注
    DPDK编译步骤
    什么是API,SDK和API之间的关系
    linux创建定时任务发送钉钉通知
    python-webdriver中添加cookie,解决添加了图片验证码的问题
    win7下CodeIgniter安装
    XAMPP环境搭建及同类推荐
    Fiddler死活抓不了HTTPS包解决办法
    XSS注入常用语句积累
  • 原文地址:https://www.cnblogs.com/confach/p/1384103.html
Copyright © 2011-2022 走看看