Reducing WinForm Memory Footprint with SetWorkingSet
Windows Forms apps are pretty bloated in terms of memory usage. The main reason .Net apps have such a huge footprint is that the JIT compiler loads when the app starts and all that bootstrap code and a ton of the WinForms engine is loaded and compiled at startup and gets loaded into your process. Beside the fact that this takes up processor cycles it also consumes a lot of memory. The JIT is pretty good about which code gets compiled – for the most part it compiles only code that actually gets run, but the framework itself pulls in a lot of code as well and that gets compiled as well. Third party controls - ditto. The result of this is that on startup your app consumes a lot of resources that you will not need any longer once the app is up and running. Actually if you access another new part of the application that part will compile later, but for many apps the startup and main application form make up a huge chunk of what gets loaded and compiled as the data engine and business objects etc. usually are among the things that get called at startup. A large percentage generally gets compiled right at startup.
If you’ve ever run your app and looked at Task Manager you might have noticed that the app starts out with a significant amount of memory. A basic WinForm app with a couple of textboxes and a button typically will run around 8 megs. If you move the form around a bit closer to 10. You then can minimize the app and it generally reduces to some really low memory usage number which slowly creeps back up as you open the form back up. What’s happening is that the app internally is adjusting the Working Set for the application which aggressively reduces the memory in use by the app. The app will reclaim what it needs, but for that moment the memory usage goes down drastically and it will stay much lower than the original startup usage.
For .Net applications this is a useful feature because there’s so much gunk that loads in .Net apps that goes away. You can force your app to minimize and pop back up to get the same effect, but this is probably not something that you want to do in your application <g>. A better way is to adjust the working set in your applications like this:
public static void SetWorkingSet(int lnMaxSize,int lnMinSize)
{
System.Diagnostics.Process loProcess = System.Diagnostics.Process.GetCurrentProcess();
loProcess.MaxWorkingSet = (IntPtr) lnMaxSize;
loProcess.MinWorkingSet = (IntPtr) lnMinSize;
//long lnValue = loProcess.WorkingSet; // see what the actual value
}
I’ve taken to calling this static method function in my code at the end of the Win form’s Load event and also in the Deactivate event. The latter is kind of cheating
private void frmWebStoreMain_Deactivate(object sender, System.EventArgs e)
{
wwUIRoutines.SetWorkingSet(750000,300000);
}
The value will not actually reduce to exactly what you tell it to, but Windows will try to reduce as much as possible. Memory usage readings in TaskManager aren’t really all that accurate, but I’ve heard from several people using some of our tools that they really were surprised by the memory usage. Doing something like the above reduced memory usage considerably.
For example, West Wind Web Monitor without the above code runs around 14-15megs. With the above code (as part of the monitoring thread) the typical memory usage runs between 5-7 megs. For the West Wind Web Store Client app without the above the app runs 25 megs. With the code it runs at 7-10megs.
In a way this is cheating the app – but it certainly improves the impression of memory usage of the application on the machine. I suspect that this has little effect on actual performance as the memory would be reclaimable anyway if the system needed it. But given that some people look at program size as an indication of the bloat in an app it can’t hurt to do this.
Note that there is some overhead in doing this. You’re freeing causing memory to be freed from the app – memory that will have to be reclaimed later if it is needed again. So be careful with this sort of thing in high performance applications.
Other Posts you might also like
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Getting the Client IP Address in ASP.NET Core
- Resolving Paths To Server Relative Paths in .NET Code
- Preventing iOS Textbox Auto Zooming and ViewPort Sizing
The Voices of Reason
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Remember that this is not something that really doesn't help drastically with memory usage - it's a hack at best and mainly for applications to 'look' more palatable when they are not active in Task manager. The actual memory usage (private bytes) may actually be higher or lower that what you see in the Working Set.
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
public abstract class SettingsProc
{
static bool m_TipAction;
static SettingsProc()
{
m_TipAction = true;
}
public static void SetWorkingSet()
{
System.Diagnostics.Process loProcess = System.Diagnostics.Process.GetCurrentProcess();
if(m_TipAction == true)
{
loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet - 1);
loProcess.MinWorkingSet = (IntPtr)((int)loProcess.MinWorkingSet - 1);
}
else
{
loProcess.MaxWorkingSet = (IntPtr)((int)loProcess.MaxWorkingSet + 1);
loProcess.MinWorkingSet = (IntPtr)((int)loProcess.MinWorkingSet + 1);
}
m_TipAction = !m_TipAction;
}
}
//.............
private void frmWebStoreMain_Deactivate(object sender, System.EventArgs e)
{
wwUIRoutines.SetWorkingSet();
}
Best regards
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: installed w/o admin rights any workaround
I installed this app on a machine where i dnt have the admin rights.I keep getting exceptions when ever i try to run the app.Is there a way to work around w/o changing any code and make it work.
Any response is appreciated.
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
loProcess.maxWorkingSet = loProcess.maxWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Additionally MSDN documentation for the same does not mention anything like. It mentions of SecurityPermission level - Permission.Unrestricted.
Is there possibly a way to use this to facilitate the same for users without admin privileges?
A quick response will be much appreciated.
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Just converted the code to VB.NET and it works GREAT!!!
Couple of notes on the conversion
Here's a VB.NET call notice the system.IntPtr conversion to make it work
SetWorkingSet(System.IntPtr.op_Explicit(CInt(750000)), System.IntPtr.op_Explicit(CInt(300000)))
Public Sub SetWorkingSet(ByVal lnMaxSize As System.IntPtr, ByVal lnMinSize As System.IntPtr)
Try
Dim loProcess As _ System.Diagnostics.Process_
= System.Diagnostics.Process._
GetCurrentProcess()
loProcess.MaxWorkingSet = lnMaxSize
loProcess.MinWorkingSet = lnMinSize
Catch
'do nothing. this only works on a machine_ onwhich the user has admin rights
End Try
END SUB
# re: Reducing WinForm Memory Footprint with SetWorkingSet
It was nice to see the same concept applied to WinForms apps, which tend to bloat up the working set very quickly.
Thanks for the tip!
# re: Reducing WinForm Memory Footprint with SetWorkingSet
i just implemented this solution but the problem is some times i got error "The parameter is incorrect" while setting MaxWorkingSet.....
this is very random.... and i only managed to reproduce it only once but still not been able to understand the cause.....
this is what i get....
at System.Diagnostics.Process.SetWorkingSetLimits(Object newMin, Object newMax)
at System.Diagnostics.Process.set_MaxWorkingSet(IntPtr value)
at MemoryOptimizer.SetWorkingSet()
any ideas???
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Take a look at the SciTech memory profiler and watch the tutorials.
You will then understand better why certain apps seem to grab a lot of memory. There are a lot of articles around the internet that describe how to write efficient dot net code.
After that there are a lot of articles posted around the internet describing the details of garbage collection.
.Net is a very powerful, productive and capable system, allowing mere mortals to build impressive systems, on the other hand, unfortunately there are a lot of developers out there that aren't taking the time to really understand how it works internally, and this is causing mis-conceptions that dot net is a memory pig. It's not dot net, it's the way the application is written that prevents garbage collection, translating into larger and larger working sets.
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Ok with your analyse but:
How to reduce memory allocation with DataTable when MaDataTable.dispose() doesn't have effect...
(escuse for my english..)
# re: Reducing WinForm Memory Footprint with SetWorkingSet
I know very well that from a developer's standpoint, the memory is released if needed by other applications. However, also as a developer, I need to make my users happy. And to them, they are only happy if the .NET application doesn't "appear" to use up that much memory. I don't care if it takes a a few more miliseconds of CPU time to re-evaluate the working set, it makes my users happy.
And as far as Garbage Collection goes, forcing Garbage Collection does not have the same affect. I have tried it as well, and though GC.Collect() forces garbage collection to occur, it does not release any memory from the working set, and therefore does not appear to release much memory at all.
# re: Reducing WinForm Memory Footprint with SetWorkingSet
Your Article help me a lot.....keep rocking.....
# re: Reducing WinForm Memory Footprint with SetWorkingSet