8

Can someone please explain why the following program runs out of memory?

class Program
{
    private static void ThreadRoutine()
    {
        System.Windows.Media.MediaPlayer player = new System.Windows.Media.MediaPlayer();
    }

    static void Main(string[] args)
    {
        Thread aThread;
        int iteration = 1;

        while (true)
        {
            aThread = new Thread(ThreadRoutine);
            aThread.Start();
            aThread.Join();

            Console.WriteLine("Iteration: " + iteration++);
        }
    }
}

To be fair, the specific exception I get is a System.ComponentModel.Win32Exception, "Not enough storage is available to process this command." The exception happens on the attempt to create a new MediaPlayer.

MediaPlayer does not implement the IDisposable interface so I'm not sure if there is other cleanup necessary. I certainly didn't find any in the MediaPlayer documentation.

Hamid Pourjam
  • 20,441
  • 9
  • 58
  • 74
Sid G.
  • 143
  • 4
  • Sorry, .NET framework 4.5. – Sid G. May 29 '15 at 18:08
  • 5
    Probably because you're creating new threads until you run out of memory, unless I'm missing something here. – Mike G May 29 '15 at 18:09
  • @mikeTheLiar the garbage collector should take care of that, the instances all loose their references. – Bart Friederichs May 29 '15 at 18:14
  • @SidG. Does it also do that if you force garbage collection after each run? – Bart Friederichs May 29 '15 at 18:14
  • @BartFriederichs - Yes, it does. After the Join I placed a Collect() followed by a WaitForPendingFinalizers(). It runs significantly slower but crashes after about the same number of iterations ~15,600. – Sid G. May 29 '15 at 18:19
  • 1
    @SidG. The strangest thing is that it seems to have an empty constructor - http://referencesource.microsoft.com/#PresentationCore/Core/CSharp/System/Windows/Media/MediaPlayer.cs,bb0167984f382321. – Eugene Podskal May 29 '15 at 18:32
  • 2
    It is not actually empty. Code for field initializers are hoisted into the constructor. Scroll down towards the bottom of the page and note the `_duceResource` member. WPF relies on dispatcher loop to get stuff automatically released, the weak event pattern is a good example. Core problem with this code is that it doesn't create a dispatcher loop. The code bombs when the quota for 'atoms' is consumed, a low-level operating resource consumed by the RegisterClassEx() winapi function. – Hans Passant May 29 '15 at 19:20
  • If I take the thread out of the equation, everything works fine, 1,000,000 iterations+. Checked it on a memory profiler, no problems. Not leaking a drop. No dispatcher loop necessary. – Sid G. May 29 '15 at 20:31
  • Well, the lack of valid answers seems to indicate that I'm out of luck with respect to finding a solution that doesn't involve a memory leak. Interestingly enough, it isn't media player, the problem seems to also exist with a parent class, DependencyObject. The Object class does not exhibit the problem so that means it's either introduced with DependencyObject or it's abstract parent class DispatcherObject. I have not found anything in documentation that points to a problem with allocating inside a thread. – Sid G. May 31 '15 at 14:19
  • Your `while (true)` loop (which creates new threads) likely executes far quicker than the OS can start & destroy these threads. (The OS only switches to other threads every few milliseconds!) So your new threads accumulate and accumulate until you've used up available resources. You're essentially trying to create an infinite number of threads, and that will obviously lead to resource pressure / exhaustion. As I see it, the garbage collector is mostly irrelevant here. The primary purpose of GC is to reclaim managed memory, not unmanaged OS resources (such as threads). – stakx - no longer contributing Jun 09 '15 at 13:16

2 Answers2

1

I had an application long ago, which rendered data to image, and placed it over a PictureBox as result, like a pipe line of graphical engine, ...

The thing ware in common with you, were the lack of memory, it was like this,...

  • 66%
  • 67%
  • 68%
  • 69%
  • 70%
  • 71%
  • 72%
  • 73%
  • 74%
  • 70% -->Auto Garbage Collecting
  • 71%
  • 72%
  • 73%
  • 74%
  • 75%
  • 76%
  • 77%
  • 78%
  • 79%
  • 80%
  • 81%
  • 77% -->Auto Garbage Collecting
  • 78%
  • 79%
  • .
  • .
  • .

And a lot of Auto Garbage Collecting at high memory around 90% to 97%,... but seem it wasn't enough, and at some point around 97% and 98% system (application) crashed with memory issue...

so i come with this idea, to call garbage collector every several frame, so i did this: GC.Collect();

GC.Collect it self is to heavy, also when there are too many object inside memory,... but when you don't left too many object, it work smooth,...

there are also many thing about finalizing objects, but I'm not sure if they work properly, as they naturally end in -> x = null

which mean, we broke the link to that object, but doesn't mean we do not have this object some where around galaxy (for example you can fill object destructor with a message, and see after leaving that object it won't get destroyed immediately, it take till you close your application / call GC.Collect / Lack on memory, or maybe random auto collect),... until it get destroyed,... so, for example as the "using" directive, it self call "Dispose" method, and we fill our dispose with obj= null,... then i'm not sure if i should recommend you writing manual finalize method, but there's still one thing... there's a Marshal object, which i don't know where it come from, and if it work over all object, i see some programmer use it to release objects, ..., i don't know how it work, but it maybe really release the object from our memory,...

If you did not found anything useful about it, my first option will be calling GC.Collect...

also you can set it's parameters, so it collect less object, than it would do normally.

https://msdn.microsoft.com/en-us/library/xe0c2357%28v=vs.110%29.aspx

Release resources in .Net C#

i also didn't read this fully, but seem he has thread and memory issue: Freeing resources when thread is not alive

Community
  • 1
  • 1
Hassan Faghihi
  • 1,888
  • 1
  • 37
  • 55
0

My guess is that the Thread objects created aren't garbage collected in time.

As you are stating in your comment that you are out of luck with respect to finding a solution that doesn't involve a memory leak, I'm posting this alternative as an answer even though it doesn't attempt to answer your original question.

If you use the ThreadPool instead, it (a) runs much faster and (b) does not crash.

class Program
{
    private static void ThreadRoutine(object state)
    {
        var player = new MediaPlayer();

        var iteration = (int)state;
        if (iteration % 1000 == 0)
        {
            Console.WriteLine("Executed: " + state);
        }
    }

    static void Main(string[] args)
    {
        for (int i = 0; i < 10000000; i++)
        {
            if (i % 1000 == 0)
            {
                Console.WriteLine("Queued: " + i);
            }

            ThreadPool.QueueUserWorkItem(ThreadRoutine, i);
        }
    }
}

On my machine, I have no problem creating 10 million thread-pool threads in a few seconds.

Queued: 9988000
Queued: 9989000
Queued: 9990000
Queued: 9991000
Executed: 9989000
Executed: 9990000
Executed: 9991000
Executed: 9988000
Queued: 9992000
Executed: 9992000
Queued: 9993000
Queued: 9994000
Queued: 9995000
Executed: 9994000
Executed: 9993000
Queued: 9996000
Executed: 9996000
Executed: 9995000
Queued: 9997000
Executed: 9997000
Queued: 9998000
Executed: 9998000
Queued: 9999000
Executed: 9999000
Press any key to continue . . .
Micke
  • 2,251
  • 5
  • 33
  • 48