HodlDwon
HodlDwon

Reputation: 1171

Can this While-Foreach loop cause a managed-memory leak?

I was reading @supercat's post here https://stackoverflow.com/a/10317035 and I had a question regarding the following:

It's important to note that even objects which only interact with 100% managed objects can do things that need to be cleaned up (and should use IDisposable). For example, an IEnumerator which attaches to a collection's "modified" event will need to detach itself when it is no longer needed. Otherwise, unless the enumerator uses some complex trickery, the enumerator will never be garbage-collected as long as the collection is in scope. If the collection is enumerated a million times, a million enumerators would get attached to its event handler.

I currently have a very very very slow memory leak in my code and I'm having a hell of a time isolating it (it takes at least 12 hours to notice a trend in private memory and at least 3 days at full-steam before it will throw an OutOfMemoryException). I think it might be related to what @supercat mentioned about internally subscribing to an event, except I am dealing with C# and not VB. In the production code the GC cleans up the memory if the while-loop exits, but not before then.

In the following code, are there any event handlers getting subscribed to behind the scenes?

Does anything in the two loops look like a managed-leak?

Does Directory.EnumerateFiles() have odd memory-usage behaviour?

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DoForeachWhile_Test
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any key to begin. Press Escape to stop the program.");
            ConsoleKeyInfo KeyPressed = Console.ReadKey();

            while (KeyPressed.Key != ConsoleKey.Escape)
            {
                IEnumerable<string> SomeIteration = Directory.EnumerateFiles(@"C:\"); // new List<string>() { "1", "2", "3", "4", "5" };

                foreach (var StringItem in SomeIteration)
                {
                    Console.WriteLine(StringItem);
                }

                //Thread.Sleep(10);
                Console.Clear();

                if (Console.KeyAvailable)
                    KeyPressed = Console.ReadKey(true);
            }

            Console.WriteLine("Press any key to exit the program.");
            Console.ReadKey();
        }
    }
}

Upvotes: 2

Views: 1233

Answers (2)

HodlDwon
HodlDwon

Reputation: 1171

After running the sample code for a day there was no leak.

Upvotes: 1

Gutblender
Gutblender

Reputation: 1350

As the comments say, I also don't think there's a memory leak in your loops.

But what about this part: What if RunningOTS.StartWork(...) throws an exception? Should you then call RunningOTS.EndWork(...) passing a possibly invalid WorkStartHandle?

Normally I would write a block like this like

try
{
    WorkStartHandle =
        RunningOTS.StartWork(p_waitForStartAcknowledge: true);

    // do {  } while ( )

    RunningOTS.EndWork(WorkStartHandle);
}
finally
{
    Log
        .FormattedLine(MessageScope.Nests,
        "{0} :: RunToken {1}", NestID, RunningOTS);
}

However, this may not work for you because I don't know if you might get an exception thrown in your do while. It may be that you get an exception thrown by a line other than RunningOTS.StartWork(...), and you do need to call RunningOTS.EndWork(...).

My best suggestion, then, is to somehow test if WorkStartHandle is valid in the finally block. Presuming this is a handle like many others,

if (WorkStartHandle >= 0) // valid handle?
    RunningOTS.EndWork(WorkStartHandle)

...may work.

Upvotes: 0

Related Questions