3

im just building a very simple event based proxy monitor top disable the proxy settings depending on if a network location is available.

the issue is that the application is a tiny 10KB and has minimal interface, but yet it uses 10MB of ram.

The code is pretty simple:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.NetworkInformation;
using Microsoft.Win32;

namespace WCSProxyMonitor
{
    class _Application : ApplicationContext
    {
        private NotifyIcon NotificationIcon = new NotifyIcon();
        private string IPAdressToCheck = "10.222.62.5";

        public _Application(string[] args)
        {
            if (args.Length > 0) 
            {
                try
                {
                    IPAddress.Parse(args[0]); //?FormatException
                    this.IPAdressToCheck = args[0];
                }
                catch (Exception) 
                {}
            }

            this.enableGUIAspects();
            this.buildNotificationContextmenu();
            this.startListening();
        }

        private void startListening() 
        {
            NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(networkChangeListener);
        }

        public void networkChangeListener(object sender, EventArgs e)
        {
            //foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
            //{
                //IPInterfaceProperties IPInterfaceProperties = nic.GetIPProperties();
            //}

            //Attempt to ping the domain!
            PingOptions PingOptions = new PingOptions(128, true);
            Ping ping = new Ping();

            //empty buffer
            byte[] Packet = new byte[32];

            //Send
            PingReply PingReply = ping.Send(IPAddress.Parse(this.IPAdressToCheck), 1000, Packet, PingOptions);

            //Get the registry object ready.
            using (RegistryKey RegistryObject = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings", true)) 
            {
                if (PingReply.Status == IPStatus.Success)
                {
                    this.NotificationIcon.ShowBalloonTip(3000, "Proxy Status", "proxy settings have been enabled", ToolTipIcon.Info);
                    RegistryObject.SetValue("ProxyEnable", 1, RegistryValueKind.DWord);
                }
                else
                {
                    this.NotificationIcon.ShowBalloonTip(3000, "Proxy Status", "proxy settings have been disabled", ToolTipIcon.Info);
                    RegistryObject.SetValue("ProxyEnable", 0, RegistryValueKind.DWord);
                }
            }
        }

        private void enableGUIAspects()
        {
            this.NotificationIcon.Icon = Resources.proxyicon;
            this.NotificationIcon.Visible = true;
        }

        private void buildNotificationContextmenu()
        {
            this.NotificationIcon.ContextMenu = new ContextMenu();
            this.NotificationIcon.Text = "Monitoring for " + this.IPAdressToCheck;

            //Exit comes first:
           this.NotificationIcon.ContextMenu.MenuItems.Add(new MenuItem("Exit",this.ExitApplication));
        }

        public void ExitApplication(object Sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}

My questions are:

  • Is this normal for an application built on C#
  • What can I do to reduce the amount of memory being used.

the application is built on the framework of .NET 4.0

Regards.

RobertPitt
  • 56,863
  • 21
  • 114
  • 161
  • When compiled down to .NET IL, you get smaller binaries, so the 10kB app size is quite normal. Not sure what to expect about the 10MB RAM usage however. – Chris O Mar 21 '11 at 11:47
  • 1
    Your code base might be 10k but you forget the framework you're using which isn't, and that is loaded as well. – Lloyd Mar 21 '11 at 11:47
  • the application was compiled at 10K I had migrated it away from the release folder to the desktop, I can understand around 2~3 MB ram, but 10MB is way to much resources for such a small app :( – RobertPitt Mar 21 '11 at 11:48
  • 3
    It also pre-grabs some RAM for your application to grow into, so 10MB is reserved but not necessarily used. – Adam Houldsworth Mar 21 '11 at 11:48
  • Adam, Is this configurable? my application does not need a lot of ram. – RobertPitt Mar 21 '11 at 12:00
  • @Robert What constraints do you have on memory use? If you are so short of memory, why have you selected a programming environment based on a VM? – David Heffernan Mar 21 '11 at 12:10
  • 1
    Unfortunately it was the only language im comfortable in to do this. – RobertPitt Mar 21 '11 at 12:21
  • see this http://stackoverflow.com/questions/223283/net-exe-memory-footprint – Rezoan Jan 01 '13 at 07:16

4 Answers4

7

It doesn't use anywhere near 10 MB of RAM. It uses 10 MB of address space. Address space usage has (almost) nothing whatsoever to do with RAM.

When you load the .NET framework, space for all the code is reserved in your address space. It is not loaded into RAM. The code is loaded into RAM in 4kb chunks called "pages" on an as-needed basis, but space for those pages has to be reserved in the address space so that the process is guaranteed that there is a space in the address space for all the code it might need.

Furthermore, when each page is loaded into RAM, if you have two .NET applications running at the same time then they share that page of RAM. The memory manager takes care of ensuring that shared code pages are only loaded once into RAM, even if they are in a thousand different address spaces.

If you're going to be measuring memory usage then you need to learn how memory works in a modern operating system. Things have changed since the 286 days.

See this related question:

Is 2 GB really my maximum?

And my article on the subject for a brief introduction to how memory actually works.

http://blogs.msdn.com/b/ericlippert/archive/2009/06/08/out-of-memory-does-not-refer-to-physical-memory.aspx

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    +1, I totally agree with the point that i need to learn how the Memory Manager works, i know off all what you said, i just do not know in detail, you have cleaned a few things up for me and im grateful – RobertPitt Mar 21 '11 at 14:37
  • It seems like reserving address space doesn't cost anything, since the address space is for that process only; why not reserve as much as possible as soon as you start the process? And why not have the OS automatically reserve 2GB (or the whatever the limit is for 64 bit machines) automatically as soon as you start a process? – configurator Mar 26 '11 at 17:05
  • @configurator: Remember, processes are shared by DLLs. Suppose the first DLL loaded reserves the entire address space. *How do you then load a second DLL?* It's code has nowhere to go! – Eric Lippert Mar 26 '11 at 19:42
  • That explains it - I thought that address space is reserved for the entire process, not for a specific DLL. – configurator Mar 26 '11 at 19:45
1

If you just start your application and then check the amount of memory usage the number may be high. .Net Application preload about 10 MB of memory when the application is started. After your app runs for a while you should see the memory usage drop. Also, just because you see a particular amount of memory in use by your app in the Task Manager it doesn't mean it is using that amount. .Net can also share memory for some components as well as preallocate memory. If you are really concerned get a real profiler for your application.

Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
  • 1
    BTW. there is about 1MB reserved per thread. Most .Net applications have 3 or more threads. – Matthew Whited Mar 21 '11 at 12:03
  • I have used a profile, the only peaks I could find was the conversion from a png to an ico for the `NotifyIcon`, I have manually converted that to an ico file and changed the code, this still changed nothing which leads me to believe your correct, I will leave the application running for a while to see what happens. also i only have the main thread (i believe) I know how expensive threads are thats why I tied it together with the GUI thread, i might add one in for the network monitoring. – RobertPitt Mar 21 '11 at 12:04
  • .Net creates a few of the threads for you. If you check you will see at least the main thread and a GC thread. You will probably also see another thread that works with events and such. – Matthew Whited Mar 21 '11 at 12:09
1

Your app itself is small, but it references classes the .NET framework. They need to be loaded into memory too. When you use Process Explorer from Sysinternals you can see what dlls are loaded and, if you select some more columns, also how much memory they use. That should help explain where some of the memory footprint is coming from, other reasons as described in the other answers may still be valid.

You could try a GC.Collect() to see how much memory is used after that, not recommended to fiddle with the GC in production code tho.

Regards GJ

gjvdkamp
  • 9,929
  • 3
  • 38
  • 46
  • Thanks for your answer, after using the software you recommended it actually has no libraries below it, but it is a child process of explorer. – RobertPitt Mar 21 '11 at 13:47
  • That's how the process are related to eachother. When you hit CTRL-D, the lower pane will show all the Dll's loaded by the selected process. Then right-click on the columns of the lower grid to add 'WS total bytes' that gives the memory footprint off all loaded dll's. Not sure tho if these will be shared with other running programs, could be they're not loaded exclusively for your process, that's where my knowledge ends. – gjvdkamp Mar 21 '11 at 14:15
0

Yes this is normal for C# applications, starting the CLR takes some doing. As for reducing this the less DLL's you load the better so see what references you can remove.

Example I see you are importing Linq but did not see any in a quick scan of code, can you remove this and reduce the number of DLL's you project depends on.

I also see that you are using windows forms, 10M is not large for any application using forms.

David Waters
  • 11,979
  • 7
  • 41
  • 76
  • 1
    Just because you have a reference and an using import statement it doesn't mean that an assembly is loaded. If you are not using System.Core (LINQ and some other stuff) then you won't have that assembly loaded in memory. – Matthew Whited Mar 21 '11 at 11:57
  • the only entities used in my App is a `NotifiyIcon`, there is no windows what so ever, @Matthew Whited, I agree there called 'references' as there only 'referenced' – RobertPitt Mar 21 '11 at 11:59
  • @RobertPitt, if you are using any code from `System.Windows.Forms` then your app will be using a lot more assemblies. – Chris O Mar 21 '11 at 12:10