当前位置: 首页 > news >正文

Hello, Memory Leak

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

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

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

如何知道内存泄漏?

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

Detected memory leaks !  
Dumping objects 
->  
{
10381 } normal block at  0x01C76F40 4  bytes  long
Data: 
<   t  >  EC 9C  74   00  
{
9443 } normal block at  0x01C37D68 4  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下会出现:

ContractedBlock.gif ExpandedBlockStart.gif Code
No memory leaks detected. 
‘xxx.exe
': Unloaded  c:\xxxx\Debug\dbghelp.dll' 
Visual Leak Detector 
is now exiting.

 上面是最简单的方法有个缺点,要是我不想讲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()也会销毁。

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

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

 

 

 

 

 

相关文章:

  • 公车理论
  • 讲述专业乞丐年薪10万的生活
  • symantec中liveuodate服务器安装
  • 转-SQL 2005修改系统表
  • PetShop的系统架构设计
  • WPF/Silverlight的UI和逻辑完全分离
  • 爆炸后的情人对白
  • 分享一个有趣的学习方法,欢迎一起探讨如何提高学习兴趣
  • IE8简体中文正式版
  • html、html服务器控件和web服务器控件的区别
  • Sun 赞助了 EclipseCon 2009!
  • 无线故障全搞定 OmniPeek使用技巧
  • 虚拟机-实验者的福音
  • 河南移动网上营业厅不支持google浏览器chrome
  • PhotoShop 学习方法论简单总结
  • [译]Python中的类属性与实例属性的区别
  • 10个最佳ES6特性 ES7与ES8的特性
  • CSS实用技巧干货
  • download使用浅析
  • Java程序员幽默爆笑锦集
  • Less 日常用法
  • Mithril.js 入门介绍
  • Quartz初级教程
  • rabbitmq延迟消息示例
  • 湖南卫视:中国白领因网络偷菜成当代最寂寞的人?
  • 基于阿里云移动推送的移动应用推送模式最佳实践
  • 计算机在识别图像时“看到”了什么?
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 入口文件开始,分析Vue源码实现
  • 小程序 setData 学问多
  • 小程序button引导用户授权
  • 用mpvue开发微信小程序
  • 正则学习笔记
  • 正则与JS中的正则
  • postgresql行列转换函数
  • (2/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (2015)JS ES6 必知的十个 特性
  • (DFS + 剪枝)【洛谷P1731】 [NOI1999] 生日蛋糕
  • (ISPRS,2023)深度语义-视觉对齐用于zero-shot遥感图像场景分类
  • (每日持续更新)jdk api之FileReader基础、应用、实战
  • (七)Knockout 创建自定义绑定
  • (七)MySQL是如何将LRU链表的使用性能优化到极致的?
  • (一)基于IDEA的JAVA基础1
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)关于如何学好游戏3D引擎编程的一些经验
  • .net core 3.0 linux,.NET Core 3.0 的新增功能
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .net 程序发生了一个不可捕获的异常
  • .NET多线程执行函数
  • .net经典笔试题
  • .NET轻量级ORM组件Dapper葵花宝典
  • [ABC294Ex] K-Coloring
  • [Android]Android开发入门之HelloWorld
  • [AUTOSAR][诊断管理][ECU][$37] 请求退出传输。终止数据传输的(上传/下载)
  • [C++]类和对象【下】