我正在研究的程序中存在内存泄漏,就提交而言,它已存在很长时间了。根据这两个解释explanation 1和explanation 2,只要使用带有_bstr_t的=赋值运算符就会导致内存泄漏。
Context - 有一个数据库对象,通常用于对数据库进行快速sql查询。每种方法最终都使用以下方法
NvStatus DbUtils::ReadFromDatabase(IUnknown * poNvData,
const std::wstring & oConnectString,
const std::wstring & oSQLStatement)
{
//some checks
_bstr_t tbtSQLStr = oSQLStatement.c_str();//memory leak
_bstr_t tbtConnStr = oConnectString.c_str();//memory leak
//pass the _bstr_t to another method and get data from DB
return status;
}
根据文章,由于_bstr_t及其创建方式,每次调用数据库时,此方法都会泄漏数据。我的问题是我该怎么做才能阻止程序炸毁并强制对_bstr_t对象进行垃圾回收?
Microsoft声明我有责任在使用后清理内存,这样我怎样才能在不破坏传递给我的数据的情况下执行此操作?我尝试对字符串进行深层复制,但失败了......任何建议都会非常感激!
经过进一步调查后,我记忆泄漏的两个热点是我首先发布的那个,希望这有帮助
static bool GetValueFromVariant(VARIANT & tvInputValue,
std::wstring & roOutputValue)
{
_bstr_t tTemp = tvInputValue.bstrVal;
if(tTemp.length()>0)
{
roOutputValue = (wchar_t*) tTemp;
}
return true;
}
注释表明这些_bstr_t应该自动清理自己...但是在调试我的Windows服务的堆大小时,堆大小不断增加,调试器继续指向所有使用这些_bstr_t对象的函数。显然,这些_bstr_t没有被清除。
更多上下文,这个内存泄漏的大部分源于一遍又一遍地创建一个COM对象,但是当我完成它时我发布了该对象并且我检查了Release()函数调用返回的引用计数,它返回0.因此,我知道我没有构建COM对象......
将wstring指向_bstr_t的地址时会出现问题吗?
答案 0 :(得分:0)
您的示例的第一行不会泄漏内存,因为您要将C样式字符串分配给_bstr_t
包装器。如果您将先前分配的BSTR
分配给_bstr_t
,情况会有所不同。这是问题,在您的第二个解释中对此进行了描述。
考虑以下情况:
void foo()
{
BSTR s1 = SysAllocString(L"String1");
_bstr_t s2 = s1;
}
此处,使用BSTR
分配本机SysAllocString
并将其放入s1。下一行从s1构造一个新的BSTR
,它放在s2中。当s2超出范围时,其析构函数调用SysFreeString
,从而解除分配副本。然而,原始的s1变量保持不变并且会泄漏。
要解决此问题,您需要让s2取得s1的所有权:
void foo()
{
BSTR s1 = SysAllocString(L"String1");
_bstr_t s2(s1, false);
}
或
void foo()
{
BSTR s1 = SysAllocString(L"String1");
_bstr_t s2;
s2.Attach(s1);
}
正如我的评论所示,_bstr_t
的析构函数将调用SysFreeString
,从而负责释放资源。
您可能无法立即看到内存释放,因为BSTR
通常是缓存的。可以通过setting an environment variable禁用此行为以进行调试。
答案 1 :(得分:0)
在第一种情况下,请不要致电SysFreeString
。没有内存泄漏。
用于的构造函数:
_bstr_t tbtSQLStr = oSQLStatement.c_str();
创建源字符串的副本,当tbtSQLStr
超出范围时,析构函数调用将释放此副本。使用类类型包装器的重点是,您不需要手动调用SysFreeString
。
请注意,根据// ....
块中代码的确切性质,您甚至可能根本不需要创建此字符串副本。
在第二种情况下(这与第一种情况真的是一个单独的问题,你应该发布了两个不同的问题),也没有内存泄漏。
tTemp
分配副本; roOutputValue
复制该副本,然后tTemp
的析构函数释放第一个副本。
然而,您通过创建和销毁tTemp
浪费了时间,您可能刚写完:
if ( SysStringLen(tvInputValue.bstrVal) > 0 )
roOutputValue = tvInputValue.bstrVal;
我假设"真实代码"实际上检查变体在此时持有BSTR。