http://blog.chinaunix.net/uid-10386087-id-2959221.html

今天遇到一个内存泄露的问题。是师兄检测出来的。Variant类型在使用后要Clear否则会造成内存泄露,为什么呢?

Google一下找到下面一篇文章,主要介绍了Com的内存泄露,中间有对Variant的一些解释吧。

1. 引用计数泄漏
由于C++的一些对象生命周期难以管理,在COM中加入了引用计数,用来解决这个问题,引用计数是COM最重要的特性 之一。但讽刺的是,虽然很多COM组件是用C++写的,但是在所有编程语言中,C++使用COM是最麻烦的,而且最容易产生引用计数的泄漏,从而最终造成 内存泄漏。

引用计数泄漏一旦产生,比new了之后忘了delete还难以定位,因为一个对象可能被很多地方使用,根本就不知道到底是那个使用的地方在 AddRef之后没有Release,只能知道对象泄漏了,但不知道是哪里导致泄漏。

因此在使用COM的时候,尽量使用智能指针,而且最好是所有使用COM的地方全部使用智能指针,这样能在很大成都上防止产生COM对象的引用计数泄 漏。

然而使用了智能指针一定能解决问题吗?也不一定,至少有以下两种情况,使用了智能指针仍然会产生引用计数泄漏,而且比上面的情况更加难以找到原因:

对象循环引用导致引用计数泄漏
如果有两个对象A和B,在A中使用了B,同时在B中使用了A,B对象中有一个智能指针指向A,A对象中有一 个智能指针指向B,这时候就会产生循环引用导致。因为A对象能被析构的前提是B对象先析构,而B对象析构的前提同样是A对象先析构,这就成了一个死局,两 个对象都无法析构,A和B都产生了内存泄漏。

要想解决这种问题,一种方法是把这个循环的链打断,在某个合适的地方把某一个对象中的智能指针显示地把智能指针赋值为NULL,这样循环的条件被打 破,两个对象就都可以析构了。另一种方法是使用弱引用,自动完成。但弱引用相对有点复杂,在本文中不作介绍,读者可以自行在网上搜索相关资料。

不正确的智能指针使用方法导致引用计数泄漏
有时候使用了智能指针,并且也没有循环引用,但是对象仍然有引用计数泄漏,是不是很困惑?对于 这种情况,有可能是因为使用智能指针不正确引起的。

我们有时候会这样使用智能指针:

pUnknown->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

spMyInterface变量是一个智能指针,在大多数情况下这样使用不会有任何问题,尤其是使用了微软的_com_ptr_t或CComPtr 等。但如果我们使用一些第三方的智能指针,例如Boost里的intrusive_ptr。我们知道,在_com_ptr_t和CComPtr中,重载 了&操作符,在&操作符中,会先释放目前的引用计数,然后返回一个指向指针的指针。这样带来的好处是使用&操作符的时候,都会先 释放当前的对象,不会造成内存泄漏,坏处是这个智能指针可能无法放到一些STL的容器中,因为有一些STL的容器,在移动容器中的数据单元时,会用 到&操作符,这样的结果是一旦移动,对象就被释放了。Boost的智能指针,例如intrusive_ptr是没有重载&操作符的,因此 可以放心地在STL容器中使用,但如果写类似于QueryInterface(__uuidof(IMyInterface), & spMyInterface)就要特别小心了,例如像下面的连续两次QueryInterface用法,就会产生引用计数泄漏:

boost::intrusive_ptr    spMyInterface;

pUnknown->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

pUnknown2->QueryInterface(__uuidof(IMyInterface), & spMyInterface);

因为第二次调用QueryInterface之前,spMyInterface并没有Release,因此就有引用计数泄漏了。如果在某个循环中这 样使用,则问题会更加严重。

要解决这类问题,有两种方法。第一是同一个变量永远不要多次使用,在变量声明周期中类似的操作只使用一次,如果有多次使用的需求,用别的变量来实 现。另一种方法是每次使用之前,都先显示地把变量清空,释放引用计数,例如先spMyInterface,再调用QueryInterface。

2. 字符串(BSTR)泄漏
字符串泄漏容易被忽视,而且COM中使用的BSTR字符串,并不是用new或者malloc等来分配的,而 是用类似于::SysAllocString等API来分配的,要检测是否有泄漏更加困难。

我们一般在调用某个类似于这样的函数GetString([out] BSTR * str)的返回BSTR的指针的函数后,需要调用::SysFreeString把字符串释放掉,和new/delete、malloc/free、 AddRef/Release类似。如果忘记调用::SysFreeString,就会产生内存泄漏。

一般我们使用字符串类来自动释放,例如使用CComBSTR,就可以这样写:

CComBSTR str;

GetString(&str);

str在析构的时候会自动调用::SysFreeString把字符串释放掉。

但如果看一下CComBSTR类的实现,我们会发现,这个类和Boost的intrusive_ptr类似,是没有重载&操作符的,这就带 来和intrusive_ptr一样的问题,如果连续两次以上使用这个字符串,就会产生内存泄漏,例如字符串类我们有时候会这样用:

CComBSTR str;

for(int I = 0;I < 100;I ++)

{

pInterface->GetString(&str);

// do something

}

这样的后果是每调用一次,就会 产生一个内存泄漏。因此一定要保证在字符串对象的生命周期中,只被使用一次,代码改为这样就不会有问题:

for(int I = 0;I < 100;I ++)

{

CComBSTR str;

pInterface->GetString(&str);

// do something

}

另外还有一种方法是使用_bstr_t的GetAddress函数,这个函数返回的是 BSTR *,每次调用里面都会先释放当前的字符串,因此使用 _bstr_t属于比较保险并且方便的选择。不过遗憾的是在VC6中这个类并没有实现GetAddress函数,用起来可能会影响代码的移植性。

3. VARIANT泄漏
这个情况和字符串泄漏类似,变量不再使用之后,应该调用::VariantClear来释放内存,否则就可能会 产生内存泄漏。

解决方法也和字符串类似,可以使用VARIANT的CComVariant,但这个类和CComBSTR也有一样的问题,因此使用的时候也一样要注 意,必须保证每个变量的生命周期中只使用一次。

另外有一个和_bstr_t类似的类:_variant_t,里面有一个GetAddress函数可以比较方便和安全的使用。具体的细节请参考这几 个类的实现代码,这里不多做介绍。

4. 总结
对于本文中提到的几种内存泄漏的原因,根源在于使用一个第三方的类的时候,其实并没有真正理解这些类的实现原理,我们以为正确 地使用了,但其实用法并不对。因此在使用一个不是自己写的第三方类的时候,不能直接拿过来就用,而是应该花一点时间去仔细了解一下这个类,知道怎样使用是 合理的,而怎样使用是不正确的,把所有的隐患在编码之前就解决掉,整个项目开发过程就会更加顺利,产品质量就会更高。

关于第三方类库的使用,在本人另一篇博文《编码原则十日谈》中有提及,这里给出地址,供读者参考。

Variant 与 内存泄露相关推荐

  1. tinyxml 内存泄露_盒子论坛

    导航: 论坛 -> 发布控件 斑竹:liumazi,ruralboy 作者: ★☆☆☆☆ - 普通会员 2012/7/6 9:52:59 标题: QDAC A3完整源码和示例 浏览:13255 ...

  2. Android中出现内存泄露的原因

    短生命周期的对象持有了长生命周期的对象,使得短生命周期对象驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再被使用,无法释放这些内存. 以下会造成内存泄露: a.数据库的cursor没有 ...

  3. Linux下正确使用getifaddrs()函数避免内存泄露

    工作中使用valgrind检测内存泄露时,发现getifaddrs()很容易导致内存泄露,下面是正确的代码: //get local ip of network card //gcc -g get_a ...

  4. 使用valgrind检测ATS插件中的内存泄露

    一.内存错误出现的场景 这几天在重构ATS插件代码的过程中遇到了烦人的内存泄露问题, 周五周六连续两天通过走查代码的方法,未能看出明显的导致内存错误的代码, 同时也觉得C和C++混合编程得到一个动态库 ...

  5. 在Ubuntu 14.04 64bit上安装Valgrind并检查内存泄露

    valgrind官网:http://valgrind.org/ 1.安装方法 第一种方式:下载目前最新的源码,编译安装,在服务器上推荐这种方式 wget http://valgrind.org/dow ...

  6. ATS插件开发中内存泄露问题的解决方法探讨

    接触ATS开发已经有几年了,开发过内核的模块,也从事过插件的开发.内存泄露问题一直是一个困扰大多数ATS开发者的头疼的问题,下面说说我自己的感受和思考.这里这关注ATS插件开发这个话题.源码的exam ...

  7. 通过define _CRTDBG_MAP_ALLOC宏来检测windows上的code是否有内存泄露

    VS中自带了内存泄露检测工具,若要启用内存泄露检测,则在程序中包括以下语句: #define _CRTDBG_MAP_ALLOC #include <crtdbg.h> 它们的先后顺序不能 ...

  8. Ubuntu下内存泄露检测工具Valgrind的使用

    在VS中可以用VLD检测是否有内存泄露,可以参考http://blog.csdn.net/fengbingchun/article/details/44195959,下面介绍下Ubuntu中内存泄露检 ...

  9. OpenCV中的内存泄露问题(cvLoadImage,cvCloneImage)

    转自:http://apps.hi.baidu.com/share/detail/30893646 在做项目的过程中,使用OpenCV经常会出现一些内存泄露问题,自己编写的程序出现问题还情有可原,但若 ...

最新文章

  1. 安装python,如果让升级版本的话
  2. 单列表_正态分布检验(单样本K-S检验)
  3. VTK:PolyData之GetMiscPointData
  4. Java的各种打包方式
  5. html输入框只能输入几个,input 两个input框只能允许同时输入一个
  6. c++ vector 一部分_为什么现在的手机都采用Type-C接口?它到底好在哪里?看完你就明白了...
  7. 聊聊如何构建一支自驱团队(二)
  8. iOS中Safari浏览器select下拉列表文字太长被截断的处理方法
  9. beetl的使用感受
  10. 记Hadoop HA高可用性分布式集群搭建过程
  11. 拉取远程分支到本地分支_想买的保险本地没有分支机构怎么办?异地投保理赔难吗?...
  12. atitit.木马病毒webshell的原理and设计 java c# .net php.
  13. 算法高级(45)-阿尔法狗到底有多厉害?
  14. 2016年世界各国gdp排名 人均gdp排名 人口排名
  15. C++ 笔试面试题 ~[有答案]
  16. 飞秋远程可利用0day 的详细分析和利用方法 飞秋漏洞
  17. macbook pro 重置SMC
  18. 鸿蒙系统分享,鸿蒙系统的启动流程学习分享
  19. C++ Primer阅读笔记--书包程序
  20. mysql qc_qc-mysql

热门文章

  1. Hibernate3.x,hibernate3.x,Hibernate3.x整合Spring3.x不能实现自动创建表结构的解决办法:...
  2. Tomcat6.0.37,tomcat6.0.37,下访问Web项目,jsp项目不需要输入项目名称和端口号的配置方法...
  3. sam格式的结构和意义_各种格式的练字本,对写字真有帮助吗
  4. 剑指offer之求1+2+...+n
  5. 《关于我的那些面经》滴滴Java岗(附答案)
  6. C++(14)--面向对象
  7. 完善博文 共享内存一写多读无锁实现的代码逻辑部分
  8. 英语口语-文章朗读Week10 Wednesday
  9. 解决:Unable to identify index name. XXXModel is not a Document. Make sure the document class is annota
  10. 解决: /bin/sh: 1: java: not found