2

I ran different tests recently on a .net app to see how memory footprint could be kept down. I came across various tips/guidelines like disposing unmanaged resources, unregistering events, using FREEZE on xaml resources etc. which all made sense. Most of the things were already taken care of , so the memory consumption remained same. I figured out however, that each new window which is never opened before in the current run, will consume some more memory and never seem to return it back after you close the window.

So I ran GC.Collect()right after the window close for debugging purpose but with no success.

In the app were a few windows with AllowsTransparency=true , so I removed the attribute and saw a big memory difference, approx.. 5MB less!, but whatever memory the window took now was still not released after the window was closed, so the problem remained the same. Here is the sample

C#

Window w;
bool isWindowOpen = false;
private void Button_Click(object sender, RoutedEventArgs e)
{

   if (!isWindowOpen)
   {
     w = new Window();
     isWindowOpen = true;

     // turn off the following two lines to see a noteable difference.
     w.AllowsTransparency = true; 
     w.WindowStyle = WindowStyle.None;

     //Even when the transparency is set to false, the memory increased by the new 
     //Window will never be returned. Try making the window a little bit heavier by
     //adding a few buttons and combos and clicking them rapidly before closing the 
     //window.

     w.Show();
   }
   else
   { w.Close(); isWindowOpen = false; GC.Collect(); 
    //Console.WriteLine(GC.GetTotalMemory(true).ToString());
    //Console.WriteLine(GC.CollectionCount(0).ToString());
   }
 }

Can any CLR/WPF guru please explain this? Is there absolutely no way I can force GC to run immediately after I close the transparent window, freeing up all the memory it consumed? I understand GC might run later when it needs to, but after all task manager is all what clients get, and there are some maniacs also.

learner
  • 565
  • 1
  • 6
  • 14
  • 4
    Just minimize the window, causing the working set to get paged out and the memory usage to appear to go down. Task Manager is not a memory profiler, and the path you're embarking on is a very wrong-headed one. – Cody Gray - on strike Mar 03 '12 at 11:45
  • Also, `GC.Collect()` doesn't actually cause garbage collection to occur. The CLR is free to ignore your request and simply mark the objects as available for collection. See [this answer](http://stackoverflow.com/questions/5191897/how-to-release-the-occupied-memory/5192350#5192350) for details. You don't write managed code if you care about absolute memory usage. The important thing is overall performance, and you're guaranteed to get a better performing app by letting the garbage collector do its own thing. It wasn't intended to be tampered with. – Cody Gray - on strike Mar 03 '12 at 11:53
  • My app could not be minimized. Its like a gadget displaying all the time on the desktop. Is there any way i can manually page out the working set? Also what options do i have if i want to use the WPF presentation power along with the care of absolute memory usage? – learner Mar 03 '12 at 12:08
  • 2
    Use a memory profiler, not task manager. – Kent Boogaart Mar 03 '12 at 12:39
  • 1
    Minimizing it was a joke. The point is that the information you're getting from Task Manager is inaccurate—laughably so. you don't really need to minimize it, you need to forget about this type of manual control over memory usage. .NET is a garbage-collected language. That means everything happens automatically. The advice you already got and discussed in the first paragraph of your question (disposing unmanaged resources, etc.) is all that you need to do. Everything else is just going to cause trouble and degrade performance. – Cody Gray - on strike Mar 04 '12 at 07:42
  • Your options to use WPF with absolute memory usage is to learn DirectDraw and other APIs, which you call from a native C++ application. Not nearly as easy as using a pre-packaged framework like WPF. – Cody Gray - on strike Mar 04 '12 at 07:44

1 Answers1

4

I found that Kernal32 dll was the answer to my problem.

[DllImport("kernel32.dll")]
private static extern bool SetProcessWorkingSetSize(IntPtr proc, int min, int max);

SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);

SetProcessWorkingSetSize with -1 values will delete as many pages as it could for the process and taskmanager figures will go down drastically. I dont know however that what performance hit does it have but for my app it is perfect.

learner
  • 565
  • 1
  • 6
  • 14
  • 1
    Yes, `SetProcessWorkingSetSize` is doing the same thing that minimizing the app is doing. The point is that **you shouldn't be doing this**. Paging out the working set is going to degrade the hell out of performance, because each time it tries to do something, it needs to page that memory back in from the disk. I don't think you understand how virtual memory works. This doesn't "free" the memory used by the app, it only releases the information from the physical RAM by paging it out to disk. It has to get paged back in order to be used again; that's slow, and why it doesn't happen by default. – Cody Gray - on strike Mar 11 '12 at 06:11