【问题标题】:High memory usage for small application小型应用程序的高内存使用率
【发布时间】:2011-07-19 15:03:06
【问题描述】:

我只是构建一个非常简单的基于事件的代理监视器顶部禁用代理设置,具体取决于网络位置是否可用。

问题是应用程序很小,只有 10KB,接口最小,但它使用 10MB 内存。

代码很简单:

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();
        }
    }
}

我的问题是:

  • 这对于基于 C# 构建的应用程序是否正常
  • 我可以做些什么来减少正在使用的内存量。

应用程序建立在 .NET 4.0 的框架上

问候。

【问题讨论】:

  • 当编译成 .NET IL 时,你会得到更小的二进制文件,所以 10kB 的应用程序大小是很正常的。但是,不确定 10MB RAM 的使用情况会怎样。
  • 您的代码库可能是 10k,但您忘记了您正在使用的框架,哪个不是,并且也已加载。
  • 应用程序以 10K 编译,我已经将它从发布文件夹迁移到桌面,我可以理解大约 2~3 MB 内存,但是对于这样一个小应用程序来说,10MB 是很多资源的方式: (
  • 它还预先获取了一些 RAM 以供您的应用程序增长,因此保留了 10MB 但不一定使用。
  • 不幸的是,这是唯一让我感到舒服的语言。

标签: c# memory-management


【解决方案1】:

它不使用接近 10 MB 的 RAM。它使用 10 MB 地址空间。地址空间使用(几乎)与 RAM 无关。

当您加载 .NET 框架时,所有代码的空间保留在您的地址空间中。它没有加载到 RAM 中。代码根据需要以称为“页面”的 4kb 块加载到 RAM 中,但这些页面的空间必须在 地址空间保留,以便进程保证有一个空间地址空间用于它可能需要的所有代码。

此外,当每个页面加载到 RAM 中时,如果您有两个 .NET 应用程序同时运行,那么它们共享该页面的 RAM。内存管理器负责确保共享代码页只加载到 RAM 中一次,即使它们位于一千个不同的地址空间中。

如果您要测量内存使用情况,那么您需要了解内存在现代操作系统中的工作原理。自 286 天以来,情况发生了变化。

查看这个相关问题:

Is 2 GB really my maximum?

还有我关于该主题的文章,简要介绍了内存的实际工作原理。

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

【讨论】:

  • +1,我完全同意我需要学习内存管理器如何工作的观点,我知道你所说的一切,我只是不知道细节,你已经清理了一些东西支持我,我很感激
  • 保留地址空间似乎不需要任何成本,因为地址空间仅用于该进程;为什么不一开始就尽可能多地预订呢?为什么不让操作系统在您启动进程时自动保留 2GB(或 64 位机器的任何限制)?
  • @configurator:记住,进程是由 DLL 共享的。假设第一个加载的 DLL 保留了整个地址空间。 然后你如何加载第二个 DLL? 它的代码无处可去!
  • 这就解释了——我认为地址空间是为整个进程保留的,而不是为特定的 DLL 保留的。
【解决方案2】:

您的应用程序本身很小,但它引用了 .NET 框架的类。它们也需要加载到内存中。当您使用Process Explorer from Sysinternals 时,您可以查看加载了哪些 dll,如果您选择更多列,还可以查看它们使用了多少内存。这应该有助于解释一些内存占用的来源,其他答案中描述的其他原因可能仍然有效。

您可以尝试GC.Collect() 来查看之后使用了多少内存,但不建议在生产代码中摆弄 GC。

问候 GJ

【讨论】:

  • 感谢您的回答,使用您推荐的软件后,实际上它下面没有库,而是资源管理器的子进程。
  • 这就是进程相互关联的方式。当您按下 CTRL-D 时,下部窗格将显示所选进程加载的所有 Dll。然后右键单击下方网格的列以添加“WS 总字节数”,从而使所有加载的 dll 的内存占用量减少。不确定这些是否会与其他正在运行的程序共享,可能它们不是专门为您的进程加载的,这就是我的知识结束的地方。
【解决方案3】:

如果您只是启动应用程序然后检查内存使用量,则该数字可能很高。 .Net 应用程序在应用程序启动时预加载大约 10 MB 的内存。在您的应用运行一段时间后,您应该会看到内存使用量下降。此外,仅仅因为您在任务管理器中看到应用程序正在使用特定数量的内存,并不意味着它正在使用该数量。 .Net 还可以为某些组件共享内存以及预分配内存。如果您真的很担心,请为您的应用程序获取一个真正的分析器。

【讨论】:

  • 顺便说一句。每个线程大约保留 1MB。大多数 .Net 应用程序都有 3 个或更多线程。
  • 我使用了一个配置文件,我能找到的唯一峰值是 NotifyIcon 从 png 到 ico 的转换,我已经手动将其转换为 ico 文件并更改了代码,这个仍然没有改变任何东西,这让我相信你的正确,我会让应用程序运行一段时间看看会发生什么。我也只有主线程(我相信)我知道线程有多昂贵,这就是为什么我将它与 GUI 线程绑定在一起,我可能会添加一个用于网络监控。
  • .Net 为您创建了一些线程。如果您检查,您将至少看到主线程和一个 GC 线程。您可能还会看到另一个处理事件等的线程。
【解决方案4】:

是的,这对于 C# 应用程序来说是正常的,启动 CLR 需要做一些事情。 至于减少这个,你加载的DLL越少越好,所以看看你可以删除哪些引用。

示例我看到您正在导入 Linq,但在快速扫描代码时没有看到任何内容,您能否将其删除并减少您项目所依赖的 DLL 的数量。

我也看到你使用的是windows forms,10M对于任何使用forms的应用程序来说都不算大。

【讨论】:

  • 仅仅因为你有一个引用和一个 using import 语句,并不意味着一个程序集被加载。如果您不使用 System.Core(LINQ 和其他一些东西),那么您不会将该程序集加载到内存中。
  • 我的应用程序中使用的唯一实体是NotifiyIcon,没有任何窗口,@Matthew Whited,我同意那里称为“引用”,因为只有“引用”
  • @RobertPitt,如果您使用来自System.Windows.Forms 的任何代码,那么您的应用将使用更多的程序集。
猜你喜欢
  • 2015-04-02
  • 1970-01-01
  • 1970-01-01
  • 2012-11-21
  • 2016-10-05
  • 2012-03-28
  • 2016-06-02
  • 2011-05-15
  • 2013-08-18
相关资源
最近更新 更多