Variant 与 内存泄露

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

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/456973.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装安全类软件进行了android签名漏洞修补,魅族MX3怎么升级固件体验最新比较稳定的版本...

魅族mx3固件怎么升级?flyme os系统会持续更新&#xff0c;升级魅族MX3手机系统需先下载MX3的升级固件&#xff0c;升级固件分为体验版和稳定版。魅族MX3固件有体验版和稳定版两种&#xff0c;顾名思义&#xff0c;体验版为最新版但相比稳定版来说存在更多的漏洞&#xff0c;升…

linux su切换用户提示Authentication failture的解决办法

由于ubtun系统默认是没有激活root用户的&#xff0c;需要我们手工进行操作&#xff0c;在命令行界面下&#xff0c;或者在终端中输入如下命令&#xff1a; sudo passwd Password&#xff1a;你当前的密码 Enter new UNIX password&#xff1a;这个是root的密码 Retype new …

@property

class Person(object):def __init__(self, name,age):#属性直接对外暴露#self.age age#限制访问self.__age ageself.__name namedef getAge(self):return self.__agedef setAge(self,age):if age<0:age 0self.__age age#方法名为受限制的变量去掉双下划线propertydef a…

ubuntu入门知识

1、linux系统发展历史 unix -> Linux -> ubuntu linux发展轨迹图 2、ubuntu下载和安装 推荐使用长期支持版本&#xff1a; 10.04,12.04,14.04或LTS版本 安装环境VMware虚拟机 3、安装之后创建root sudo passwd root 输入root用户密码即可 4、安装软件&#xff1a; 更新软…

html 二级试题,计算机二级考试WEB试题及答案

计算机二级考试WEB试题及答案当前主要的 WEB数据库访问技术有哪些?答&#xff1a;到目前为止&#xff0c;WEB数据库访问技术主要分为两大类&#xff1a;(1)公共网关接口技术(CGI);CGI 是 WEB 服务器运行时外部程序的规范&#xff0c;按照 CGI 编写的程序可以扩展服务器的功能&…

细数阿里云服务器的十二种典型应用场景

原文链接&#xff1a;http://click.aliyun.com/m/13910/免费开通大数据服务&#xff1a;https://www.aliyun.com/product/odps文章转载&#xff1a;小白杨1990如今&#xff0c;阿里云的产品可谓是多种多样&#xff0c;纷繁复杂。面对各种各样的技术和产品&#xff0c;ECS、RDS、…

动态给实例添加属性和方法

from types import MethodType#创建一个空类 class Person(object):__slots__ ("name","age","speak","height")per Person() #动态添加属性&#xff0c;这体现了动态语言的特点(灵活&#xff09;per.name "tom" print(…

android导入项目出现style错误,menu错误

android导入项目出现style错误&#xff0c;menu错误 style //查看 res/values/styles.xml 下的报错点。<style name"AppBaseTheme" parent"Theme.AppCompat.Light"> //把这个改成 <style name"AppBaseTheme" parent"android:The…

Vim的基本操作总结

最近在学习Linux基础的时候&#xff0c;对Vim的基本操作时遇到很多问题&#xff0c;如编辑错误&#xff0c;无法退出Vim等。通过一系列的学习后才解决了这些问题&#xff0c;希望这个过程能对后来者有所帮助 先对Vim的三种模式做个大致的介绍&#xff1a; Vi有三种基本工作模式…

html股票数据代码,股票数据的网站抓取(4.2)代码优化

#codingutf-8from selenium import webdriverimport timeimport osimport reimport sysimport threadingimport Queueimport Tkinter as tkfrom selenium.common.exceptions import NoSuchElementExceptiondef myinit():reload(sys)sys.setdefaultencoding(utf8)#获取屏幕分辨率…

对象属性和类属性

class Person(object):#这里的属性实际上属于类属性&#xff08;用类名调用&#xff09;name "person"def __init__(self,name):#对象属性self.name nameprint(Person.name) per Person("tom") #对象属性的优先级高于类属性 print(per.name) #动态的给对…

commons-fileupload、smartUpload和commons-net-ftp

1.本地上传 在许多Web站点应用中都需要为用户提供通过浏览器上传文档资料的功能&#xff0c;例如&#xff0c;上传个人相片、共享资料等。在DRP中&#xff0c;就有这个一个功能&#xff0c;需要将对应的物料图片上传并显示。对于上传功能&#xff0c;其实在浏览器端提供了很好的…

11月14号站立会议(从即日14号起到24号截至为final阶段工作期)

小组名称&#xff1a;飞天小女警 项目名称&#xff1a;礼物挑选小工具 小组成员&#xff1a;沈柏杉&#xff08;组长&#xff09;、程媛媛、杨钰宁、谭力铭 代码地址&#xff1a;HTTPS:https://git.coding.net/shenbaishan/GIFT.git SSH&#xff1a;gitgit.coding.net:shenbais…

初学大数据之Pycharm常用的快捷键总结

pycharm快捷键及一些常用设置&#xff0c;有需要的朋友可以参考下。 加粗的都是个人认为比较常用的快捷键 AltEnter 自动添加包 Ctrlt SVN更新 Ctrlk SVN提交 Ctrl / 注释(取消注释)选择的行 CtrlShiftF 高级查找 CtrlEnter 补全 Shift Enter 开始新行 TAB ShiftTAB…

摇一摇 声音 html5,HTML5摇一摇以及音频播放问题优化总结

前言感想&#xff1a;不放过任何一个WARNING、ERROR或者不够好的体验点&#xff0c;持续不断优化&#xff0c;精益求精&#xff0c;我们就能够得到提高。1. 摇一摇不够灵敏、摇动很多次没有响应的问题、原来摇一摇代码是从网络Copy的&#xff0c;活动上线后&#xff0c;发现部分…

调用API发送短信python

import http.client import urllibhost "106.ihuyi.com" sms_send_uri "/webservice/sms.php?methodSubmit"# 用户名是登录用户中心->验证码短信->产品总览->APIID account "xxxxxxxx" # 密码 查看密码请登录用户中心->验证码短…

JAVA内部类使用

一、什么是内部类&#xff1f; 一个类的定义放在另一个类的内部&#xff0c;这个类就叫做内部类 二、内部类有那些特性&#xff1f; 1、内部类仍然是一个独立的类&#xff0c;在编译之后内部类会被编译成独立的.class文件&#xff0c;但是前面冠以外部类的类名和$符号 。  2、…

初学大数据之模块集成:Pycharm安装numpy,scipy,sklearn等包时遇到的各种问题的一键解决方法

最近在学习机器学习&#xff0c;要用Python写程序&#xff0c;习惯了用IDE软件&#xff0c;所以就使用Pycharm软件。但是在导入类似numpy,sklearn等模块的时候&#xff0c;发现了各种问题&#xff08;如Python版本与模块之间的兼容等各类问题&#xff09;,上网找了许多方法&…

html 圆环实现多种颜色,SVG实现多彩圆环倒计时效果的示例代码

圆环倒计时我们经常见到&#xff0c;实现的方法也有很多种。但是本文将介绍一种全新的实现方式&#xff0c;使用SVG来实现倒计时功能。本文主要用到了SVG的stroke-dasharray和stroke-dashoffset特性。下图是倒计时运行效果&#xff1a;SVG倒计时案例下面说说相关的实现代码。cs…

调用API发送邮件163邮箱Python

#发邮件的库 import smtplib# from email.mime.text import MIMEText #SMTP服务器 SMTPSever "smtp.163.com" #发邮件的地址 sender "18332191389163.com" #发送这邮箱的密码 passwd "xxxxxxxx"#设置发送的内容 message "liu wang is …