代码之家  ›  专栏  ›  技术社区  ›  Robert Fraser

减少.NET应用程序的内存使用?

  •  106
  • Robert Fraser  · 技术社区  · 16 年前

    减少.NET应用程序内存使用的一些技巧是什么?考虑以下简单的C程序。

    class Program
    {
        static void Main(string[] args)
        {
            Console.ReadLine();
        }
    }
    

    编译在 释放 模式 X64型 在Visual Studio外部运行时,任务管理器报告以下内容:

    Working Set:          9364k
    Private Working Set:  2500k
    Commit Size:         17480k
    

    如果只是为了 x86 :

    Working Set:          5888k
    Private Working Set:  1280k
    Commit Size:          7012k
    

    然后,我尝试了以下程序,该程序执行相同的操作,但在运行时初始化后尝试调整进程大小:

    class Program
    {
        static void Main(string[] args)
        {
            minimizeMemory();
            Console.ReadLine();
        }
    
        private static void minimizeMemory()
        {
            GC.Collect(GC.MaxGeneration);
            GC.WaitForPendingFinalizers();
            SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
                (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
        }
    
        [DllImport("kernel32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetProcessWorkingSetSize(IntPtr process,
            UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
    }
    

    关于结果 x86 释放 在Visual Studio外部:

    Working Set:          2300k
    Private Working Set:   964k
    Commit Size:          8408k
    

    这有点好,但对于这样一个简单的程序来说,它似乎仍然是多余的。有什么诀窍可以使C过程更精简一些吗?我正在编写一个程序,该程序大部分时间都是在后台运行的。我已经在一个单独的 Application Domain 这意味着用户界面的东西可以安全地卸载,但是当它只是坐在后台时占用10MB的空间似乎太大了。

    附笔。 至于为什么我会关心---(电源)用户往往担心这些事情。即使它对性能几乎没有影响,半技术熟练的用户(我的目标受众)也倾向于对后台应用程序内存的使用产生嘶嘶的感觉。甚至当我看到Adobe更新程序占用了11MB的内存时,我也会感到很奇怪,甚至在游戏时,它也可以占用不到6MB的内存。我知道,在现代操作系统中,从技术上讲,这些东西实际上并不重要,但这并不意味着它对感知没有影响。

    9 回复  |  直到 11 年前
        1
  •  32
  •   Community Mohan Dere    9 年前
    1. 您可能需要检查堆栈溢出问题 .NET EXE memory footprint .
    2. msdn博客帖子 Working set != actual memory footprint 就是要解除工作集、处理内存的神秘化,以及如何对RAM消耗总量进行精确计算。

    我不会说您应该忽略应用程序的内存占用——显然,更小、更高效的内存占用是可取的。但是,您应该考虑您的实际需求。

    如果您正在编写一个标准的Windows窗体和WPF客户机应用程序,这些应用程序将运行在个人的PC上,并且很可能是用户操作的主要应用程序,那么您可以避免在内存分配方面过于懒散。(只要它全部被释放。)

    但是,要解决这里的一些人说不要担心的问题:如果您正在编写一个Windows窗体应用程序,它将在终端服务环境中运行,在一个可能由10个、20个或更多用户使用的共享服务器上运行,那么是的,您必须考虑内存使用。你需要保持警惕。解决这一问题的最佳方法是采用良好的数据结构设计,并遵循有关何时以及分配什么内容的最佳实践。

        2
  •  44
  •   Brian Rasmussen    16 年前

    与本机应用程序相比,.NET应用程序将具有更大的占用空间,因为它们都必须在进程中加载运行时和应用程序。如果你想要真正整洁的东西,.net可能不是最好的选择。

    但是,请记住,如果您的应用程序大部分时间处于休眠状态,那么所需的内存页将从内存中交换出来,因此在大多数情况下,这并不是系统的负担。

    如果您想保持较小的内存占用,就必须考虑内存使用情况。以下是一些想法:

    • 减少对象的数量,并确保对任何实例的保留时间不要超过所需的时间。
    • 意识到 List<T> 以及类似的类型,在需要时将容量加倍,因为它们可能导致高达50%的浪费。
    • 您可以考虑在引用类型上使用值类型,以在堆栈上强制使用更多内存,但请记住,默认堆栈空间仅为1 MB。
    • 避免使用超过85000字节的对象,因为它们将进入未压缩的LOH,因此很容易被碎片化。

    无论如何,这可能不是一个详尽的清单,而是几个想法。

        3
  •  16
  •   JaredPar    16 年前

    在这种情况下,您需要考虑的一件事是clr的内存开销。每个.NET进程都会加载clr,因此会考虑内存因素。对于这样一个简单/小的程序,clr的成本将支配您的内存占用。

    与这个基线程序的成本相比,构建一个真正的应用程序并查看它的成本将更有指导意义。

        4
  •  7
  •   Donut    16 年前

    本身没有具体的建议,但您可以看看 CLR Profiler (免费从Microsoft下载)。
    一旦你安装好了,看看这个 how-to page .

    从操作方法:

    如何向您展示如何使用 用于调查您的 应用程序的内存分配 轮廓。可以使用clr profiler 识别导致记忆的代码 问题,如内存泄漏和 垃圾过多或效率低下 收集。

        5
  •  6
  •   Eric Petroelje    16 年前

    可能需要查看“真实”应用程序的内存使用情况。

    与Java类似,无论程序大小如何,运行时的开销都是固定的,但在那一点之后内存消耗会更合理。

        6
  •  4
  •   Jonathan Leffler    12 年前

    还有一些方法可以减少这个简单程序的私有工作集:

    1. 提交你的申请。这将从您的进程中除去JIT编译成本。

    2. 使用mpgo训练应用程序 reducing memory usage 然后去做。

        7
  •  2
  •   Peter Mortensen Pieter Jan Bonestroo    14 年前

    有很多方法可以减少你的足迹。

    有一件事你必须一直生活在 .NET 您的IL代码的本机映像的大小是 巨大的

    并且此代码不能在应用程序实例之间完全共享。偶数 NGEN'ed 程序集不是完全静态的,它们还有一些需要抖动的小部件。

    人们也倾向于编写比需要的时间长得多的代码来阻塞内存。

    一个常见的示例:使用DataReader,将内容加载到数据表中,以便将其写入XML文件。您可以很容易地遇到内存不足的异常。或者,您可以使用一个xmltextwriter,在数据阅读器中滚动,在数据库光标中滚动时发出xmlnodes。 这样,在内存中只有当前的数据库记录及其XML输出。它永远不会(或不太可能)获得更高的垃圾收集生成,因此可以重用。

    同样的情况也适用于获取一些实例的列表,做一些事情(产生数千个新实例,这些实例可能在某个地方保持引用),即使之后您不需要它们,但在foreach之后仍然引用所有内容。 显式地将输入列表和临时副产品设为空意味着,即使在退出循环之前,也可以重用此内存。

    C有一个称为迭代器的优秀特性。它们允许您通过滚动输入来传输对象,并且只保留当前实例,直到获得下一个实例为止。即使使用LINQ,您仍然不需要仅仅因为希望对其进行过滤就将其保留在周围。

        8
  •  1
  •   Peter Mortensen Pieter Jan Bonestroo    16 年前

    解决标题中的一般问题,而不是 具体问题:

    如果使用的COM组件返回大量数据 (比如大型2xn双精度数组)只有一小部分 然后可以编写一个包装COM组件, 从.NET中隐藏内存并仅返回 是需要的。

    这就是我在主要应用程序中所做的 显著提高了内存消耗。

        9
  •  0
  •   Stuart Welch    11 年前

    我发现,在长时间运行的进程中,使用setprocessworkingsetsize或emptyworkingsetapi强制内存页定期地到磁盘,可能会导致机器上所有可用的物理内存有效地消失,直到机器重新启动。我们将.NET dll加载到本机进程中,该进程将使用EmptyWorkingSet API(替代使用setProcessWorkingSetSize)在执行内存密集型任务后减少工作集。我发现,在1天到一周的时间内,一台机器在任务管理器中显示99%的物理内存使用率,而没有显示任何进程使用任何重要的内存使用率。很快,机器就会失去响应,需要重新启动。说有二十多台Windows Server 2008 R2和2012 R2服务器同时运行在物理和虚拟硬件上。

    也许将.NET代码加载到本机进程中与此有关,但使用EmptyWorkingSet(或SetProcessWorkingSetSize)的风险由您自己承担。也许在应用程序初始启动后只使用一次。我决定禁用代码,让垃圾收集器自己管理内存使用情况。

    推荐文章