使用_bstr_t进行内存泄漏,无法修复
在我正在研究的这个程序中存在内存泄漏,就提交而言,它已经存在很长一段时间了。根据这两个解释explanation 1和explanation 2每当使用=赋值运算符_bstr_t会导致内存泄漏。使用_bstr_t进行内存泄漏,无法修复
上下文 - 有一个数据库对象,通常用于执行数据库的快速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的地址时会出现问题吗?
您的示例的第一行不泄漏内存,因为您将一个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,可以禁用此行为用于调试目的。
像你说的那样,它应该释放用于这些字符串的内存,但它永远不会......这个程序将继续泄漏内存,直到所有可用内存被系统用完。该程序再一次是Windows服务,不知道这是否有所作为。根据vs2015调试器,我还添加了另一部分泄漏代码。 –
另一个想法:'_bstr_t'被引用计数。你可能将'_bstr_t'通过值传递给一个方法或一个类的实例,它被存储在哪里?这可能是一个可能的解释,为什么Dtor没有被触发,因为ref-count还没有达到0。 – Aurora
所以这一切都是因为一个COM对象被创建并创建时,它是否已经存在或未被重新初始化,并且重新初始化的步骤会导致对上述方法的调用。也许对象的大小正在增加,但根据vs2015调试器,所有变量的大小都是相同的,但堆空间会增加每个快照。 –
在第一种情况下,根本不要拨打SysFreeString
。没有内存泄漏。
构造用于:
_bstr_t tbtSQLStr = oSQLStatement.c_str();
创建源字符串的一个副本,这个副本是由析构函数调用时tbtSQLStr
超出范围释放。使用类类型包装的重点在于,您不需要手动调用SysFreeString
。
请注意,根据// ....
块中代码的确切性质,您可能甚至不需要创建该字符串的副本。
在第二种情况下(这是真的到了第一种情况下一个单独的问题,您应该已经张贴2个不同的问题),也没有内存泄漏。
tTemp
分配副本; roOutputValue
复制副本,然后tTemp
的析构函数释放第一个副本。
但是您可以通过创建和销毁tTemp
浪费的时间,你可以只写:
if (SysStringLen(tvInputValue.bstrVal) > 0)
roOutputValue = tvInputValue.bstrVal;
在那里我假设了“真正的代码”其实检查变种保持在这一点上BSTR。
是的,sysalloc和sysfree对于BSTR是有意义的,而_bstr_t是为我做的这个包装。其次,底部的代码是逐字代码的项目代码。最后,当我将一个远程调试器附加到我的代码中时,唯一显示堆栈视图最大大小的两个点源自ReadFromDatabase和GetValueFromVariant ...变量的所有大小保持完全相同,但内存增加.. –
此外,当我浏览堆栈视图时,我使用下拉菜单查看堆空间大小增加源于_bstr_t,它是_bstr_t :: _ bstr_t - > _bstr_t :: Data_t :: Data_t,因此堆大小增加是因为这些_bstr_t的我可以在调试器中清楚地看到它 –
@RAZ_Muh_Taz好吧,你应该添加代码来检查'VT_BSTR'是变体类型(我认为有一个宏可以这样做),然后才能真正使用bstr –
'_bstr_t'是本地'BSTR'的智能包装,负责内存分配和释放。因此你不应该在'_bstr_t'上调用'SysFreeString',因为它的析构函数会处理这个问题。 – Aurora
那么如果内存泄露发生,如果它们在完成时应该记住它们的内存呢?我已经研究了vs2015调试器,并且内存增加源于使用这些_bstr_t对象的所有内容...为什么它们不被清理?有没有办法强制垃圾收集他们? @Aurora –
我想我的问题归结为 - 是否有使用bstr而无需调用新的? @Aurora –