Solved

Can't find a memory leak

Posted on 2004-03-29
5
345 Views
Last Modified: 2013-11-20
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
Comment
Question by:Argonaut
5 Comments
 
LVL 14

Accepted Solution

by:
wayside earned 250 total points
Comment Utility
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
 
LVL 44

Assisted Solution

by:Karl Heinz Kremer
Karl Heinz Kremer earned 250 total points
Comment Utility
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

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Suggested Solutions

Here is how to use MFC's automatic Radio Button handling in your dialog boxes and forms.  Beginner programmers usually start with a OnClick handler for each radio button and that's just not the right way to go.  MFC has a very cool system for handli…
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

728 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now