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.pswp.cn/news/456973.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/456973.shtml
英文地址,請注明出處:http://en.pswp.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 …