• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 381
  • Last Modified:

Can't find a memory leak

I have a windows service that has a nagging memory leak.  I am not the code author so there are things I don't understand yet.  If I watch memory usage in task manager I see the available memory slowly decrease as my process usage increases.  My process then seems to release the memory but the system available never reclaims it.  Eventually all memory is used.  I found that the use of CreateThread is not recommended so I tried beginthreadex but that did not help.  I'm thinking that maybe a thread is not completeing successfully and then not being released.  But since I'm not really a C++ or threading expert I'm not sure what to do.  Here is the main cpp file, I can post others if required.


void CAISController::Start()
{
      long lCount = 0;
      long lVal = 0;
      long lReasonCode = 0;
      CString csMsgID;
      CString csRequest;
      CString csReply;
      CString csErrorMsg;
      CQueueManager objQueueManager;
      HANDLE hThreadHandle;
      HANDLE hReplyThreadHandle;
      DWORD dwThreadID = 0;
      DWORD dwSuspendCount = 0;
      DWORD dwExitCode = 0;
      tagReplyStruct* pReplyStruct = NULL;
      tagRequestStruct* pRequestStruct = NULL;
      int nIndex = 0;
      int nReplyIndex = 0;
      int nConnIndex = 0;
      int nCount = 0;
      CPtrArray colReplyStructHandles;
      CPtrArray colStaticConnectPoolHandles;
      void* pContext = NULL;
      CStaticConnectionPool* pobjStaticConnectionPool = NULL;
      CTiming* pobjTiming = NULL;
      MSG msg;
      
      // initializes cae for multi-threaded static sql
      sqleSetTypeCtx(SQL_CTX_MULTI_MANUAL);

      try
      {
            // create max request thread semaphore to limit the total request threads
            g_hMaxRequestThreadSemaphore = CreateSemaphore(NULL,
                                                                                 g_objApp->GetAppThreadMax(),
                                                                                 g_objApp->GetAppThreadMax(),NULL);

            // create max database connection semaphore to limit the total connections
            g_hMaxConnectionSemaphore = CreateSemaphore(NULL,  // no security attributes
                                                                              g_objApp->GetDBMaxConnections(),
                                                                              g_objApp->GetDBMaxConnections(),
                                                                              NULL);  // unnamed semaphore


            //call the CodeText Build
            g_objCodeText.Build();
            if (g_objErrorLog->HasError())
            {
                  // System error logged during build
                  // Log a page error to notify support and shutdown
                  g_bShutDown = true;
                  csErrorMsg = _T("A severe error was encountered during service start.");
                  csErrorMsg += _T(" The service will now shutdown.");
                  g_objErrorLog->Add(0, _T("AISController::Start()"),
                        csErrorMsg, CErrorLog::SYSTEM_EMAIL_PAGE);
            }

            objQueueManager.ConnectQueueManager();
            if (g_objErrorLog->HighestSeverity() < CErrorLog::SYSTEM_EMAIL_PAGE)
            {
                  // it's ok to continue if only a "warning" type error occured
                  colStaticConnectPoolHandles.SetSize(g_objApp->GetDBConnectionPools());
                  nCount = colStaticConnectPoolHandles.GetSize();

                  for(nIndex = 0; nIndex < nCount; nIndex++)
                  {
                        // establish Database connections
                        pobjStaticConnectionPool = new CStaticConnectionPool;
                        pobjStaticConnectionPool->Build(g_objErrorLog);
                        if (g_objErrorLog->HasError())
                        {
                              // System error logged during build
                              // Log a page error to notify support
                              // Shutdown the service
                              g_bShutDown = true;
                              csErrorMsg = _T("A severe error was encountered during service start.");
                              csErrorMsg += _T(" The service will now shutdown.");
                              g_objErrorLog->Add(0, _T("AISController::Start()"),
                                    csErrorMsg, CErrorLog::SYSTEM_EMAIL_PAGE);
                        }
                        colStaticConnectPoolHandles.SetAt(nIndex, pobjStaticConnectionPool);
                  }

                  colReplyStructHandles.SetSize(g_objApp->GetReplyQueueThreads());
                  nCount = colReplyStructHandles.GetSize();

                  for(nIndex = 0; nIndex < nCount; nIndex++)
                  {
                        // create Reply Threads
                        pReplyStruct = new tagReplyStruct;

                        // multi-threaded call
                        hReplyThreadHandle = ::CreateThread(NULL,
                                                                          0,
                                                                          &MultiThreadProcessReply,
                                                                          (LPVOID)pReplyStruct,
                                                                          THREAD_PRIORITY_NORMAL,
                                                                          &dwThreadID);

                        colReplyStructHandles.SetAt(nIndex, pReplyStruct);
                  }

                  nReplyIndex = 0;
                  nConnIndex = 0;
            }

            while (g_bShutDown != true)
            {

                  if (pRequestStruct == NULL)
                        pRequestStruct = new tagRequestStruct;                  
                  
                  pRequestStruct->csMessage = objQueueManager.GetRequestMessage(&pRequestStruct->cMsgID[0]);

                  pRequestStruct->pobjTiming->Checkpoint(CTiming::AIS_CONTROLLER);

                  if (pRequestStruct->csMessage != "" && g_objErrorLog->HighestSeverity() < CErrorLog::SYSTEM_EMAIL_PAGE)
                  {
                        //thread should go to sleep if all request threads are in use
                        WaitForSingleObject(g_hMaxRequestThreadSemaphore, INFINITE);  // no timeout period

                        if (nReplyIndex > (colReplyStructHandles.GetSize() - 1))
                              nReplyIndex = 0;
                        
                        pRequestStruct->pReplyStruct = (tagReplyStruct*)colReplyStructHandles.GetAt(nReplyIndex);

                        if (nConnIndex > (colStaticConnectPoolHandles.GetSize() - 1))
                              nConnIndex = 0;
                        
                        pRequestStruct->pobjStaticConnectionPool = (CStaticConnectionPool*)colStaticConnectPoolHandles.GetAt(nConnIndex);

                        // spawn the Request Thread
                        hThreadHandle = ::CreateThread(NULL,
                                                           0,
                                                                     &MultiThreadProcessRequest,
                                                                     (LPVOID)pRequestStruct,
                                                                     THREAD_PRIORITY_NORMAL,
                                                                     &dwThreadID);

                        ::CloseHandle(hThreadHandle);

                        nReplyIndex++;
                        nConnIndex++;
                        pRequestStruct = NULL;
                  }
                  // check to make sure that there are no messages
                  if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE) != 0)
                  {
                        // if it is a quit message then set the global
                        // variable to true
                        if (msg.message == WM_QUIT)
                              g_bShutDown = true;
                        DispatchMessage(&msg);
                        
                  }
            }
            
            if (pRequestStruct != NULL)
                  delete pRequestStruct;
 
            objQueueManager.DisconnectQueueManager();

            nCount = colReplyStructHandles.GetSize();


            // wake up each thread so it can kill itself and exit normally
            for(nIndex = 0; nIndex < nCount; nIndex++)
            {
                  pReplyStruct = (tagReplyStruct*)colReplyStructHandles.GetAt(nIndex);
                  // Now that the thread has to die or rather commit sucide, set the event to signaled
                  ::SetEvent(pReplyStruct->hDataAvailableEvent);
            }

            // now wait a couple of seconds until all threads are complete
            ::Sleep(5000);

            // get rid of Reply structures
            for(nIndex = 0; nIndex < nCount; nIndex++)
            {
                  pReplyStruct = (tagReplyStruct*)colReplyStructHandles.GetAt(nIndex);
                  if (pReplyStruct !=NULL)
                        delete pReplyStruct;
            }

            colReplyStructHandles.RemoveAll();

            // delete Static Connections
            nCount = colStaticConnectPoolHandles.GetSize();
            for (nIndex=0; nIndex < nCount; nIndex++)
            {
                  pobjStaticConnectionPool = (CStaticConnectionPool*)colStaticConnectPoolHandles.GetAt(nIndex);
                  pobjStaticConnectionPool->Destroy(g_objErrorLog);
                  delete pobjStaticConnectionPool;
            }

            colStaticConnectPoolHandles.RemoveAll();

            g_objCodeText.Destroy();

      }
      catch(...)
      {
            TRACE("\nCaught Exception in AISController::Start()\n");      
            g_objErrorLog->Add(GetLastError(), _T("AISController::Start"),
                  _T("Unknown Exception Caught"), CErrorLog::SYSTEM_WARNING);
      }

}
0
Argonaut
Asked:
Argonaut
2 Solutions
 
waysideCommented:
You can stare at code looking for a memory leak until your eyeballs shrivel up and fall out of their sockets, and never find it. :)

The only way to track these down are to use one of the tools that are made for this. Here's an MSDN link that shows you how to use the facilities built into VC++:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvc60/html/memleaks.asp

In addition there are commercially available tools such as BoundsChecker and Purify which are really good at this. They are well worth the investment, you might want to check them out.

BoundsChecker: http://www.compuware.com/products/devpartner/bounds.htm
Purify: http://www-136.ibm.com/developerworks/rational/products/purifyplus

Good luck!


0
 
Karl Heinz KremerCommented:
wayside is right, there is very little value in staring at source code to identify memory leaks. If you would know how to spot them, chances are you would not have programmed it :-)
I don't know if you can use the VC++ memory leak debugger for a server process, but it's worth a shot. I've used Purify a few years ago: It's relatively expensive, but it's a great tool. There are cheaper memory debuggers available, but I don't have any experience with them.
For "normal" stuff, the VC++ debugger is good enough. The advantage of Purify is that you can also find other types of memory errors, before the application crashes. VC++ does not tell you if you have a potential buffer overflow, you actually need to crash the application to find this problem.

If you have no idea where the memory is leaked, it's very likely that it's not in the function you've posted: It does not look like you are doing a lot of memory allocations in this function. Look for functions that use "new" or "malloc" to allocate memory. Then make sure that every piece of allocated memory gets released again.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now