Question

I have a Windows service that periodically check database for new records and processes each record in a new thread (it may take up to 10 minutes). When the service is idle right after it's started it takes like 4 MB of RAM. When it starts processing the first record it goes up to 70+ MB and stays there even when the thread is finished (I guess that's OK, because this memory can soon be needed again). Then on the next request it goes from 70 to about 100 MB and also stays there after the thread has completed. So my question is can there be a memory leak in something like this:

public partial class MyWinService : ServiceBase
{
    DBService service;
    IEnumerable<long> unfinishedRequests;
    List<long> activeRequests;

    protected override void OnStart(string[] args)
    {
        System.Timers.Timer timer1 = new System.Timers.Timer(60000);
        timer1.Elapsed += Timer_Tick;
        timer1.Start();
        service = new DBService();
        activeRequests = new List<long>();
    }

    private void Timer_Tick(object sender, System.Timers.ElapsedEventArgs e)
    {
            unfinishedRequests = service.GetUnfinishedRequests();
            foreach (var req in unfinishedRequests)
            {
                new Thread(delegate() { ProcessRequest(req); }).Start();
            }
    }

    private void ProcessRequest(long requestID)
    {
        activeRequests.Add(requestID);
        // Lots of webservice calls, XML->XSLT->HTML->PDF, byte arrays, database INSERTs & UPDATEs etc.
        activeRequests.Remove(requestID);
    }
}

Shouldn't all objects created in ProcessRequest() method be destroyed after the thread finishes? And if the program already has 100 MB of memory after the first thread why should it ask for more (in my tests the input data were identical so both threads should use the same amount of memory, I think).

Was it helpful?

Solution

Turns out I was too much relying on static helper classes. Once I changed statics to instances the memory consumption decreased noticeably and GC also was able to free much more memory

OTHER TIPS

It is not easy to answer your question without knowing more details of ProcessRequest function.

You can try to find the memmory leak issue by yourself using any of the memory profiler available

ants-memory-profiler

What Are Some Good .NET Profilers?

There are at least two things that might appear as memory leaks.

First, your activeRequests list is continually added to, but never resized. Although you remove items from the list, the memory allocated to hold those items is never reclaimed. If you added a million items to the list and then removed them all, the underlying memory to store those items would still be allocaed.

Still, your list would have to be pretty large (about 4 million items) to make up that 30 megabytes.

You said that your timer tick method could take up to 10 minutes to complete. You have a problem there because although the timer tick will complete pretty quickly, the threads that it creates might take a very long time to execute. And a minute later another timer tick will come along and queue more threads. You have no limit on threads here, so if there are a thousand unfinished requests you'll spool up 1,000 threads. That's definitely not a good thing, and it's quite possible to run out of memory that way.

You should look into using Parallel.ForEach in that loop. For example:

Parallel.ForEach(unfinishedRequests, req => ProcessRequest(req));

That will limit the number of concurrent threads, which will prevent you from having so many threads that you run out of memory or cause you system to slow down due to excessive task switching.

I don't know if what you're calling a memory leak is actually a memory leak. If you're using Task Manager to determine the amount of memory that the process is using, you're not measuring the real memory usage. Task Manager is not at all useful for finding a memory leak unless you're really leaking badly. Search SO for information about memory leak detection in C# for some ideas.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top