0

There is a memory leak in this program I am working on and it's been around for quite some time as far as the commits are concerned. According to these two explanations explanation 1 and explanation 2 whenever using the = assignment operator with _bstr_t causes a memory leak.

Context - there is a database object that is commonly used to do quick sql queries of the database. Every method eventually uses the following method

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;
}

According to articles this method will leak data every time it's called to query the database because of _bstr_t and how they are created. My question is what can I do to prevent the program from blowing up and force garbage collection on the _bstr_t objects?

Microsoft states that it's my responsibility to clean up the memory after using it so how can I do this without corrupting the data being passed to me? I tried doing a deep copy of the string but that failed... Any suggestions would be much appreciated!

After further investigation the two hot spots for my memory leak are the one I posted first and this one, hope this helps

static bool GetValueFromVariant(VARIANT & tvInputValue,
    std::wstring & roOutputValue)
{
    _bstr_t tTemp = tvInputValue.bstrVal;
    if(tTemp.length()>0)
    {
        roOutputValue = (wchar_t*) tTemp;
    }
    return true;
}

Comments suggest that these _bstr_t should clean themselves up automatically... however when debugging the heap size for my windows service, the heap size increases constantly and the debugger continues to point to functions that all use these _bstr_t objects. Clearly these _bstr_t aren't being cleaned up.

More context, the majority of this memory leak stems from creating a COM object over and over again, however I am releasing the object when I am finished with it and i check the reference count returned by Release() function call, it returns 0. Therefore I know that i don't have a build up of COM objects...

Can there be an issue when pointing a wstring to the address of a _bstr_t?

RAZ_Muh_Taz
  • 4,059
  • 1
  • 13
  • 26
  • `_bstr_t` is a smart wrapper for native `BSTR`s, taking care of memory allocation and deallocation. You're therefore not supposed to call `SysFreeString` on a `_bstr_t`, since its destructor will take care of this. – Aurora Sep 14 '17 at 19:51
  • then how come the memory leak occurs if they are supposed to take of their memory when done? I have attatched the vs2015 debugger and the memory increase stems from everything using these _bstr_t objects... why aren't they being cleaned up? Is there no way to force garbage collection on them? @Aurora – RAZ_Muh_Taz Sep 14 '17 at 20:21
  • i guess my question boils down to - is there anyway to use a bstr without calling new??? @Aurora – RAZ_Muh_Taz Sep 14 '17 at 20:33
  • When you call `SysFreeString` on a `BSTR` (i.e. via `_bstr_t`'s dtor), its allocated memory won't be released immediately. This might be the reason why you're observing a leak. You can disable this behavior by setting an environment variable `OANOCACHE=1`. See [this](https://web.archive.org/web/20070510220416/http://support.microsoft.com/kb/139071) link for further details. – Aurora Sep 14 '17 at 20:35
  • you're confusing _bstr_t with BSTR. use _bstr_t everywhere, remove all SysAllocXXX and SysFreeXXX, and you'll have zero memory leak. – Simon Mourier Sep 15 '17 at 06:02

2 Answers2

0

The first lines of your example don't leak memory, since you're assigning a C-style string to a _bstr_t wrapper. Things would look different, if you'd assign a previously allocated BSTR to a _bstr_t. This is the problem, which is described in your second explanation.

Consider the following case:

void foo()
{    
    BSTR s1 = SysAllocString(L"String1");
    _bstr_t s2 = s1;
}

Here, a native BSTR is allocated using SysAllocString and placed into s1. The next line constructs a new BSTR from s1, which is placed in s2. When s2 falls out of scope, its destructor calls SysFreeString, thereby deallocating the copy. The original s1 variable, however, remains intact and will leak.

To remedy this, you'll need to let s2 take ownership of s1:

void foo()
{ 
    BSTR s1 = SysAllocString(L"String1");
    _bstr_t s2(s1, false);
} 

or

void foo()
{
    BSTR s1 = SysAllocString(L"String1");         
    _bstr_t s2;
    s2.Attach(s1);
}

As indicated in my comments, _bstr_t's destructor will call SysFreeString, thereby taking care of deallocating resources.

You may not witness the memory deallocation right away, since BSTRs are usually cached. This behavior can be disabled for debugging purposes by setting an environment variable.

Aurora
  • 1,334
  • 8
  • 21
  • like you said it should deallocate the memory used for those strings but it never does... this program will continue to leak memory until all the available memory is used up by the system. Once more is that this program is a windows service, not sure if that makes a difference. I also added another section of code that leaks as well according to the vs2015 debugger. – RAZ_Muh_Taz Sep 14 '17 at 22:17
  • Another idea: a `_bstr_t` is reference counted. Are you perhaps passing your `_bstr_t` by value to a method or an instance of a class, where it gets stored? That would be a possible explanation, why the dtor isn't triggered, since the ref-count wouldn't have reached 0 yet. – Aurora Sep 14 '17 at 23:12
  • so this all happens because of a COM object that gets created and when it gets created, whether it exists already or not gets reinitialized and the steps for re-initialization cause the calls to the methods described above. perhaps the objects are increasing in size but according to the vs2015 debugger the size of all the variables are the same but the heap space increases every snapshot. – RAZ_Muh_Taz Sep 14 '17 at 23:36
  • there is also this Aclayers.dll that responsible for about a 3rd of the heap space increase but i searched online and nothing has told me what exactly that dll is used for. – RAZ_Muh_Taz Sep 14 '17 at 23:38
  • `_bstr_t s2 = s1;` uses the constructor, not the assignment operator. In a declaration the `=` symbol is not the assignment operator. – M.M Sep 15 '17 at 00:08
0

In the first case, simply don't call SysFreeString. There is no memory leak.

The constructor used in:

_bstr_t tbtSQLStr = oSQLStatement.c_str();

creates a copy of the source string, and this copy is freed by the destructor call when tbtSQLStr goes out of scope. The whole point of using a class type wrapper is so that you do not manually need to call SysFreeString.

Note that, depending on the exact nature of the code in the // .... block, you may have not even needed to create this copy of the string.


In the second case (which is really a separate question to the first case, you should have posted 2 different questions), there is also no memory leak.

tTemp allocates a copy; roOutputValue copies that copy, and then tTemp's destructor frees the first copy.

However you wasted time by creating and destroying tTemp, you could have just written:

if ( SysStringLen(tvInputValue.bstrVal) > 0 )
    roOutputValue = tvInputValue.bstrVal;

where I'm assuming the "real code" actually checks the variant holds a BSTR at this point.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • yeah the sysalloc and sysfree makes sense for BSTR and that _bstr_t is a wrapper that does this for me. Second, the code at the bottom is verbatim the code in the project. Finally, when i attatch a remote debugger to my code, the only 2 spots that show the stack view as the most size stem from the ReadFromDatabase and GetValueFromVariant... All the sizes of the variables remain the exact same but the memory increases... – RAZ_Muh_Taz Sep 15 '17 at 15:52
  • furthermore when i go through the stacks view and I use the drop down to see where the heap space size increase stems from _bstr_t, it's _bstr_t::_bstr_t -> _bstr_t::Data_t::Data_t so the heap size increase is because of these _bstr_t's i can see it clearly in the debugger – RAZ_Muh_Taz Sep 15 '17 at 16:24
  • @RAZ_Muh_Taz OK, you should add code to check that `VT_BSTR` is the variant type (I think there's a macro to do that) before actually using the bstr – M.M Sep 16 '17 at 06:35