sklein
asked on
Why NO memory leak with AllocSysString() in COM object???
I have build a COM wrapper for an underlying C++ DLL in order to access it with VB6 and .NET, but I am puzzled by why I'm NOT having a memory leak for one of the methods I wrote.
I have run the VB snippet below with 100,000 random strings of length 10,000 (a total of 1GB of strings allocated via AllocSysString(). Whether the FreeString() method is called or not, there is NO difference in the memory footprint of the running process (either memory usage or VM size) -- in both cases, over the entire run, the memory usage is 7.2MBytes and the VM size is 2.8MBytes.
As I understand it, it should be necessary for the VB program to free the memory allocated in the wrapper by AllocSysString(). But it looks like (somehow) that memory is being deallocated without my having to do so.
Here's my question -- can someone explain whether either (1) this test is flawed such that there really isn't a bunch of memory allocated, or (2) some part of VB6 or the COM mechanism is deallocating the memory for me, or (3) something else even more mysterious is happening such that I am not seeing 1GB of memory leak.
Thanks in advance.
I have run the VB snippet below with 100,000 random strings of length 10,000 (a total of 1GB of strings allocated via AllocSysString(). Whether the FreeString() method is called or not, there is NO difference in the memory footprint of the running process (either memory usage or VM size) -- in both cases, over the entire run, the memory usage is 7.2MBytes and the VM size is 2.8MBytes.
As I understand it, it should be necessary for the VB program to free the memory allocated in the wrapper by AllocSysString(). But it looks like (somehow) that memory is being deallocated without my having to do so.
Here's my question -- can someone explain whether either (1) this test is flawed such that there really isn't a bunch of memory allocated, or (2) some part of VB6 or the COM mechanism is deallocating the memory for me, or (3) something else even more mysterious is happening such that I am not seeing 1GB of memory leak.
Thanks in advance.
COM Wrapper code:
STDMETHODIMP CETModule::SetInString(LONG VarNum, BSTR Value)
{
// Get the specified Input variable
IVariable *pVar = pModule->GetInVariable(VarNum);
USES_CONVERSION;
if (pVar == NULL)
// Invalid VarNum
return E_INVALIDARG;
else
pVar->SetString(W2A(OLE2T(Value)));
return S_OK;
}
STDMETHODIMP CETModule::GetOutString(LONG VarNum, BSTR* Result)
{
// Get the specified Output variable
IVariable *pVar = pModule->GetOutVariable(VarNum)
if (pVar == NULL)
// Invalid VarNum
return E_INVALIDARG;
else
// MUST BE DEALLOCATED BY CETModule::FreeString()
*Result = CString(pVar->GetString()).AllocSysString();
return S_OK;
}
STDMETHODIMP CETModule::FreeString(BSTR Value)
{
try
{
SysFreeString(Value);
}
catch(...)
{}
return S_OK;
}
VB6 Code:
...
Dim index as Long
Dim out As String
For index = 1 To NumLoops
' push a random string into the COM object
obj.SetInString 0, RandomString(StrSize)
' just transfer the input var to the output var
obj.Calculate
' retrieve the random input string
out = obj.GetOutString(0)
If chkDealloc = 1 Then obj.FreeString (out)
Next index
MsgBox "Done.", vbOKOnly
...
ASKER
Friedrich--
What I am looking for is primarily an understanding of why I am NOT seeing a memory leak. As far as I can tell, the code sample I posted should leak. But it doesn't.
In this situation, reference counting is not an option. My example illustrates the sort of thing I need to do. The strings returned are dynamically created and there are potentially an unlimited number of them created during the COM object's lifetime. But they have a very short lifespan. Strict allocate/deallocate works better (I believe) than reference counting.
My question is still why it doesn't look like I need to do the deallocate!
What I am looking for is primarily an understanding of why I am NOT seeing a memory leak. As far as I can tell, the code sample I posted should leak. But it doesn't.
In this situation, reference counting is not an option. My example illustrates the sort of thing I need to do. The strings returned are dynamically created and there are potentially an unlimited number of them created during the COM object's lifetime. But they have a very short lifespan. Strict allocate/deallocate works better (I believe) than reference counting.
My question is still why it doesn't look like I need to do the deallocate!
You have not read my mail properly. You should not give the client access to the intenal structures of your server. You always generate fresh BSTR as return value.
Get a decent book about COM and check it. I suggest Essential COM....
You always have reference counting in COM that's the base of IUnknown...
Regards
Friedrich
Get a decent book about COM and check it. I suggest Essential COM....
You always have reference counting in COM that's the base of IUnknown...
Regards
Friedrich
ASKER
Friedrich--
That is not the issue here. I have implemented a COM object only because the underlying DLL is not easily accessible to VB6 or C#. It is strictly for internal consumption. I don't have the option of rewriting the underlying DLL. So, COM it is.
Second, this COM object will create (and return) an UNLIMITED number of dynamically generated strings. I have no control over that. Reference counting only adds overhead in tracking them.
Third, my question is why I'm NOT seeing memory leaks. Allocate/deallocate is a valid C/C++ style whether or not it is recommended for COM. In this case, it *appears* that the deallocate is not necessary. Does VB6 automatically deallocate [out, retval] BSTRs when they go out of scope or is something else happening here?
That is not the issue here. I have implemented a COM object only because the underlying DLL is not easily accessible to VB6 or C#. It is strictly for internal consumption. I don't have the option of rewriting the underlying DLL. So, COM it is.
Second, this COM object will create (and return) an UNLIMITED number of dynamically generated strings. I have no control over that. Reference counting only adds overhead in tracking them.
Third, my question is why I'm NOT seeing memory leaks. Allocate/deallocate is a valid C/C++ style whether or not it is recommended for COM. In this case, it *appears* that the deallocate is not necessary. Does VB6 automatically deallocate [out, retval] BSTRs when they go out of scope or is something else happening here?
If you COM server obey the rules than you do not have to worry about memory handling in Visual Basic. So if you have the typelibrary at hand VB runtime takes care of "getting" rid of the garbage. You however provided a facility to free a Servers allocated String to Client code and this is a bug.
Friedrich
Friedrich
ASKER
Friedrich--
I added the FreeString call because the sources I consulted indicated that every AllocSysString() call must be matched by an explicit SysFreeString() call. If that is not actually necessary in this case, I'd be happy to delete the (unnecessary) FreeString() method. I am trying to determine if I really don't need to do an explicit deallocation, because none of the documentation I've seen makes that claim.
Normally, the VB runtime deallocates strings that it allocates. I have not seen the claim that it deallocates strings that are passed to it thru a COM interface. Perhaps that is what is happening, because (as I've indicated) I don't see any memory leaks.
So, are you making the claim that the combination of the VB Runtime and the COM interface result in the strings being automatically deallocated? If so, does that also apply to .NET + COM (per my original question)?
I added the FreeString call because the sources I consulted indicated that every AllocSysString() call must be matched by an explicit SysFreeString() call. If that is not actually necessary in this case, I'd be happy to delete the (unnecessary) FreeString() method. I am trying to determine if I really don't need to do an explicit deallocation, because none of the documentation I've seen makes that claim.
Normally, the VB runtime deallocates strings that it allocates. I have not seen the claim that it deallocates strings that are passed to it thru a COM interface. Perhaps that is what is happening, because (as I've indicated) I don't see any memory leaks.
So, are you making the claim that the combination of the VB Runtime and the COM interface result in the strings being automatically deallocated? If so, does that also apply to .NET + COM (per my original question)?
ASKER
Friedrich--
As an alternative strategy, if I specify that the string returned by a GetOutString() call must be copied before the next GetOutString() call, then the protocol can be that GetOutString() deallocates the *previous* allocated string before allocating the next string. Then, there is never more than one string allocated. The destructor also knows what to clean up. Ugly, but guaranteed not to leak. Thoughts?
As an alternative strategy, if I specify that the string returned by a GetOutString() call must be copied before the next GetOutString() call, then the protocol can be that GetOutString() deallocates the *previous* allocated string before allocating the next string. Then, there is never more than one string allocated. The destructor also knows what to clean up. Ugly, but guaranteed not to leak. Thoughts?
The COM rule are strict and they are known in Visual Basic too. What you have to do is keep track of the memory in C/C++ code. So indeed ifyou get back a BSTR as out you know that it's your respnsibility to free it.
The typelibrary informs Visual Basic about that and so you do not have to call the SysFreeString in Visual Basic
If you like you can help the runtime with setting the returned value to nil.
You still have you memory leak if you do not free the allocates memory on the server side, and this is much more serious. You generte COM object and every time you set the string you are leaking memory, for that you have to keep track of the reference count and provide a clean up in the Release Element of the IUnknown interface.
That's absolutly necessary to get right if you do use COM...
COM and net have interoperability laysers which should be handled in .NET as in the visual basic runtime. But you still have to provide the freein of required resources in the Server. That's what I wrote, Clien and Server must no hamper with the data of the other side. This just can go wrong
For a book about COM and net check .NET and COM the Complete Interoperability Guide
Regards
Friedrich
Regards
Friedrich
The typelibrary informs Visual Basic about that and so you do not have to call the SysFreeString in Visual Basic
If you like you can help the runtime with setting the returned value to nil.
You still have you memory leak if you do not free the allocates memory on the server side, and this is much more serious. You generte COM object and every time you set the string you are leaking memory, for that you have to keep track of the reference count and provide a clean up in the Release Element of the IUnknown interface.
That's absolutly necessary to get right if you do use COM...
COM and net have interoperability laysers which should be handled in .NET as in the visual basic runtime. But you still have to provide the freein of required resources in the Server. That's what I wrote, Clien and Server must no hamper with the data of the other side. This just can go wrong
For a book about COM and net check .NET and COM the Complete Interoperability Guide
Regards
Friedrich
Regards
Friedrich
ASKER
Friedrich--
Microsoft doesn't agree with you. Please see the MSDN article: "Allocating and Releasing Memory for a BSTR" (http://msdn.microsoft.com/en-us/library/xda6xzx7(VS.71).aspx)
The intro section says:
When you create BSTRs and pass them between COM objects, you must take care in treating the memory they use in order to avoid memory leaks. When a BSTR stays within an interface, you must free its memory when you are done with it. However, when a BSTR passes out of an interface, the receiving object takes responsibility for its memory management.
The last sentence says that it is the client who is responsible for memory management in this specific case.
So, the model I am following is specifically per Microsoft recommendations. And back to my original question (which is still unanswered), why am I not seeing a memory leak when I don't do the deallocate? Since my code is consistent with Microsoft recommendations, can we stop discussing the need to free the memory on the server side?
Following is the specific Microsoft example from the above MSDN article which looks exactly like my GetOutString().
Microsoft doesn't agree with you. Please see the MSDN article: "Allocating and Releasing Memory for a BSTR" (http://msdn.microsoft.com/en-us/library/xda6xzx7(VS.71).aspx)
The intro section says:
When you create BSTRs and pass them between COM objects, you must take care in treating the memory they use in order to avoid memory leaks. When a BSTR stays within an interface, you must free its memory when you are done with it. However, when a BSTR passes out of an interface, the receiving object takes responsibility for its memory management.
The last sentence says that it is the client who is responsible for memory management in this specific case.
So, the model I am following is specifically per Microsoft recommendations. And back to my original question (which is still unanswered), why am I not seeing a memory leak when I don't do the deallocate? Since my code is consistent with Microsoft recommendations, can we stop discussing the need to free the memory on the server side?
Following is the specific Microsoft example from the above MSDN article which looks exactly like my GetOutString().
When you implement a function that returns a BSTR, allocate the string but do not free it. The receiving function releases the memory. For example:
// Example shows using MFC's
// CString::AllocSysString
//...
HRESULT CMyClass::get_StatusText( BSTR * pbstr )
{
try
{
//m_str is a CString in your class
*pbstr = m_str.AllocSysString( );
}
catch (...)
{
return E_OUTOFMEMORY;
}
// The client is now responsible for freeing pbstr.
return( S_OK );
}
//...
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Friedrich--
I think I agree with your analysis that the VB6 runtime is doing the deallocate call. That would be consistent with behavior for strings allocated in VB itself (it doesn't matter whether the string is allocated within VB or an arbitrary COM object).
This does explain why I see no memory leaks even though allocating more than 1GB of strings. Thanks.
I think I agree with your analysis that the VB6 runtime is doing the deallocate call. That would be consistent with behavior for strings allocated in VB itself (it doesn't matter whether the string is allocated within VB or an arbitrary COM object).
This does explain why I see no memory leaks even though allocating more than 1GB of strings. Thanks.
Regards
Friedrich