Solved

Memory Leak

Posted on 2003-12-07
6
1,604 Views
Last Modified: 2013-12-14
Hello all,
   I'm having a problem with a memory leak in a COM object that I can not identify.  From a VB app I create a new instance of the Request object and set a value to the SubjectCompanyName property.  That is the extent of the test application.  After VB6.exe is closed the following memory leaks appear.  When attempting to declare a CRequest::~CRequest() destructor the compiler generates an error indicating that the destructor already has a body.  So a couple of questions come to mind

1. Can I be assured that any constructor code that allocates memory will be freed by the destructor even though I can not define it?
2. How do I resolve this memory leak?

Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Info: AfxDllCanUnloadNow returning S_OK
Detected memory leaks!
Dumping objects ->
{78} normal block at 0x03814F10, 12 bytes long.
 Data: <Tx          > 54 78 1C 00 00 00 00 00 01 00 00 00
{77} normal block at 0x03814EC8, 12 bytes long.
 Data: <,x          > 2C 78 1C 00 00 00 00 00 01 00 00 00
{76} normal block at 0x03814E80, 12 bytes long.
 Data: < x          > 04 78 1C 00 00 00 00 00 01 00 00 00
{75} normal block at 0x03814E38, 12 bytes long.
 Data: <4           > 34 CD 1B 00 00 00 00 00 01 00 00 00
{74} normal block at 0x03814DF0, 12 bytes long.
 Data: <            > EC A7 1B 00 00 00 00 00 01 00 00 00
{73} normal block at 0x03814DA8, 12 bytes long.
 Data: <            > EC D6 1B 00 00 00 00 00 01 00 00 00
{72} normal block at 0x03814D60, 12 bytes long.
 Data: <            > FC 17 19 00 00 00 00 00 01 00 00 00
{71} normal block at 0x03814CF8, 40 bytes long.
 Data: <  s   s     `M  > F0 BA 73 03 8C BA 73 03 00 00 00 00 60 4D 81 03
{70} normal block at 0x03814CB0, 12 bytes long.
 Data: < L   L      > B0 4C 81 03 B0 4C 81 03 CD CD CD CD
{69} normal block at 0x03814C58, 28 bytes long.
 Data: <  s D s         > 80 BB 73 03 44 BB 73 03 00 00 00 00 CC CD CD CD
{68} normal block at 0x03814C10, 12 bytes long.
 Data: < L   L      > 10 4C 81 03 10 4C 81 03 CD CD CD CD
{67} normal block at 0x03814BB8, 28 bytes long.
 Data: <  s D s         > 80 BB 73 03 44 BB 73 03 00 00 00 00 CC CD CD CD
Object dump complete.
The thread 0xFC8 has exited with code 0 (0x0).
The thread 0xB04 has exited with code 0 (0x0).
The thread 0xFE8 has exited with code 0 (0x0).
The thread 0xFB4 has exited with code 0 (0x0).
The program 'C:\Program Files\Microsoft Visual Studio\VB98\VB6.EXE' has exited with code 0 (0x0).

**********************************************************************************
**********************************************************************************

Below is the code


*************************************
Request.cpp
*************************************
CRequest::CRequest()
{
      CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
      CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

STDMETHODIMP CRequest::get_SubjectCompanyName(BSTR *pVal)
{
      //*pVal = m_SubjectCompanyName.copy();
      *pVal = m_SubjectCompanyName;

      return S_OK;
}

************************************
Request.h
************************************
class ATL_NO_VTABLE CRequest :
      public CComObjectRootEx<CComSingleThreadModel>,
      public CComCoClass<CRequest, &CLSID_Request>,
      public ISupportErrorInfo,
      public IDispatchImpl<IRequest, &IID_IRequest, &LIBID_Client>
{
public:
      CRequest();


DECLARE_REGISTRY_RESOURCEID(IDR_REQUEST)

DECLARE_PROTECT_FINAL_CONSTRUCT()

BEGIN_COM_MAP(CRequest)
      COM_INTERFACE_ENTRY(IRequest)
      COM_INTERFACE_ENTRY(IDispatch)
      COM_INTERFACE_ENTRY(ISupportErrorInfo)
END_COM_MAP()

// ISupportsErrorInfo
      STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);

// IRequest
public:
      STDMETHOD(get_SubjectCompanyName)(/*[out, retval]*/ BSTR *pVal);
      STDMETHOD(put_SubjectCompanyName)(/*[in]*/ BSTR newVal);
      
private:
      BSTR m_SubjectCompanyName;
      CComObject<CAttributes> *m_SubjectAttributes;
      CComObject<CAddress> *m_SubjectAddress;
};

Thanks all.
0
Comment
Question by:edc
6 Comments
 
LVL 12

Expert Comment

by:vascov
ID: 9893070
You don't show the implementation of put_subject...

Don't forget that when your object dies, someone has to make sure that the memory owned by the BSTR is freed...

Use CComBSTR if you can...

HTH
0
 
LVL 1

Author Comment

by:edc
ID: 9893518
Hi HTH,
   Thanks for the info.  I have changed my BSTR to CComBSTRs, and that it the better way to do it.  After some monkeying around I found that the culprits are infact the instanciations of the other COM objects used in this com object.

CRequest::CRequest()
{
     CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
     CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

When I commented out these lines the memory leaks dissappear.  Since I instanciate these in the constructor, normally I would clean them up in the destructor, but the constructor seems to be implemented somewhere else, and I can't write a destructor in my code.  Any recommendations on how to fix this area?  I would prefer not to move them to the function level, but I won't rule that out.

Thanks much.
0
 
LVL 13

Expert Comment

by:SteH
ID: 9895390
With VC++6.0 SP5 I get this behaviour from time to time using DLLs. The allocation done insided the dll is not deallocated before the main app ends. Therefor a memory leak is reported. But inside the DLL it might be deallocated only at a later time than the reporting. It could be the same for you since the allocation numbers {67} ... {78} are relatively low.

When including
static struct _test
{
    _test()
    {
        _CrtSetBreakAlloc (78);  
    }
   
    ~_test()
    {
    }
} test1;
in your cpp file of the main app you might be able to stop when the allocation of that memory happens and see whether this memory is deallocated only later.


0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 1

Expert Comment

by:Tobber
ID: 9896017
Hi.

Since you inherit from CComObjectRootEx you can override CComObjectRootEx::FinalRelease(). There you can realese your m_SubjectAttributes and m_SubjectAddress pointers.

You should also move the creation of your aggregated objects from the constructor to an override of FinalConstruct().

Good luck.

/T
0
 
LVL 1

Author Comment

by:edc
ID: 9896558
Fantastic.  Thanks Tobber.  One question...  I used delete in the FinalRelease() to destroy the COM objects created in FinalConstruct().  It took care of the memory leaks, and exited without a problem, but is this the correct way to destroy COM objects after using ::CreateInstance() ?

Thanks
0
 
LVL 1

Accepted Solution

by:
Tobber earned 125 total points
ID: 9902419
Hi edc.

>is this the correct way to destroy COM objects after using ::CreateInstance()?

Well, COM objects destroy themselves. All users of a COM object (clients) call AddRef() to increase the objects reference count. This is done under the surface once for each of your aggregated objects when you call ::CreateInstance(). The VB client who creates the Request instance also calls AddRef() through the IUnknown interface from which CRequest inherits.

So, once the VB client has created an instance of the Request COM object all three, CRequest, CAttributes and CAddress, have reference counts equal to 1.

When a client is done using a COM object it should call Release() on the object. This was done by VB on the Request instance. When Request instance's reference count becomes zero it no longer needs its CAttributes and CAddress members, thus it should call Release() on both of them.

Your code should look something like this:

HRESULT CRequest::FinalConstruct()
{
  // Called by COM framework when the CRequest reference count goes from 0 to 1.
  HRESULT hRes = CComObject<CAttributes>::CreateInstance(&m_SubjectAttributes);
  if (hRes != S_OK)
    return hRes;
   
  return CComObject<CAddress>::CreateInstance(&m_SubjectAddress);
}

void CRequest::FinalRelease()
{
  // Called by COM framework when the CRequest reference count goes from 1 to 0.
  if (m_SubjectAddress != 0)
    m_SubjectAddress->Release();
   
  if (m_SubjectAttributes != 0)
    m_SubjectAttributes->Release();
}


Look at CComObject::Release() in atlcom.h and you'll see the following:
STDMETHOD_(ULONG, Release)()
{
  ULONG l = InternalRelease();
  if (l == 0)
    delete this;
  return l;
}

Thus, if the reference count is zero it deletes itself. Bye bye memory leaks!


Hope this suffice as an answer to your question.

/T
0

Featured Post

Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Writing a parser for java language 4 83
Unable to open debugger port in Intellij idea 6 324
C++ question 3 70
oracle 11g 23 106
What is C++ STL?: STL stands for Standard Template Library and is a part of standard C++ libraries. It contains many useful data structures (containers) and algorithms, which can spare you a lot of the time. Today we will look at the STL Vector. …
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

809 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question