【问题标题】:Are .NET Applications naturally memory intensive?.NET 应用程序自然是内存密集型的吗?
【发布时间】:2009-10-08 21:16:05
【问题描述】:

我有生以来第一次开始用 C# 编写一个大型应用程序。我编写了示例模块来测试我的软件背后的想法。该模块包含几十个 C# 字典和对象列表,每个对象都有多个成员和属性。

令我震惊的是,在初始化核心对象后,它最终使用了大约 40MB 的 RAM。

我测试发现在对象初始化之后分配了超过 30MB,但我的印象是考虑到我的对象的大小,应该不会超过几百 KB。

与本机代码应用程序相比,我做错了什么还是 .NET 天生占用大量内存?

【问题讨论】:

  • 我同意你的看法。我认为你做错了什么。
  • 你能发布一些示例代码吗?我认为这将极大地帮助理解是代码导致它单枪匹马还是被运行时复杂化
  • @Jed,heavy 是一个相对术语。今天的普通 PC 有多少 RAM?

标签: c# memory


【解决方案1】:

您如何确定使用了多少内存?众所周知,.NET 应用程序会急切地保留比所需更多的内存,只要有足够的可用内存,如果内存开始不足,就会将内存释放回系统。

我想你可能会在this MSDN article 中得到一些指点。

【讨论】:

  • 通过使用任务管理器 - 感谢指出它不能是一个有效的数字
【解决方案2】:

使用任务管理器查看内存使用情况可能非常不准确。

相反,请使用合适的内存分析工具(dotTrace 非常好,有 10 天试用期)并花时间查看您的实际内存消耗。

我在自己的代码中看到的内容包括

  • 低估了我实际使用的内存量(未正确计算单独的对象,不允许列表具有“备用”容量)
  • 保留对我不需要的临时对象的引用
  • 不允许在操作期间创建尚未被垃圾回收的瞬态对象
  • 不允许未分配的内存 - 已从操作系统申请的内存(因此在任务管理器中显示为进程的一部分)但尚未分配给任何一个对象
  • 不允许线程堆栈(每个线程都有自己的堆栈;.NET 应用程序始终是多线程的,因为框架可能会创建自己的多个线程)。

【讨论】:

    【解决方案3】:

    在我们在机器上拥有几 GB RAM 的时代,像您这样的面向用户的应用程序通常使用 C# 构建应该只使用几个 100K 的 RAM 的想法是倒退的。系统上任何未使用的内存都被浪费了。

    考虑到这一点,C# 使用与 C++ 非常不同的策略分配内存,因此分配和释放较大块的频率较低。

    也就是说,40 似乎有点高。对于非常简单的控制台应用程序,我已经习惯了接近 10-14Mb 的空间。也许有一个表格占用了 ram,或者我使用的是不同版本的框架(2.0)。

    【讨论】:

    • "系统上任何未使用的内存都被浪费了。"我的应用程序不占用的任何 RAM 都可能被其他应用程序或操作系统更好地使用。
    • 但它并没有被“占用”,它是由运行时以贪婪的方式分配的。如果需要,运行时也可以将其返回给操作系统。
    • linux 性能优于 windows 的主要原因之一是因为它使用所有未使用的内存作为磁盘缓存,大大提高了读取速度(这是非常必要的,因为 *nix 习惯使用空文件作为标志)。如果您的应用占用了这些内存,它就无法做到这一点。
    • Windows 也有预取/缓存策略。也许性质不同,但它肯定会使用多余的 RAM。例如,Windows 7 目前正在使用我的 2 GB RAM,并且 Visual Studio 等应用程序会立即加载。
    【解决方案4】:

    .Net 运行时有相当大的开销——我们发现,即使是简单的应用程序也倾向于使用比用 C++ 编写的类似应用程序更多的内存。幸运的是,随着整个代码大小的增加,这种开销很快就消失在噪音中了。第二个因素是垃圾收集,垃圾收集器“随时”运行,因此与 C++ 相比,内存分配通常不会立即释放,而是在感觉需要时释放。

    【讨论】:

      【解决方案5】:

      我想说,除非您有非常充分的理由担心 RAM 使用,否则不要。与优化一样,您应该只优化存在客户/最终用户需要这样做。我不相信现在用户会受到内存的限制(除非您谈论的是嵌入式系统),以至于他们会注意到或非常关心丢失的 38 MB。

      【讨论】:

      • 如果我在编写单个模块后看到使用了 40 兆,并且应用程序将...假设 20 - 30 个这样的模块将完整数据集相乘,那么 - 我担心我不会在 512 兆字节的 RAM 中
      • @PiotrK:是的,如果有那么简单,但事实并非如此。 .NET 应用程序由运行时管理。使用的内存量并不能直接反映您对代码所做的事情,您的假设是不正确的。
      • PiotrK:但你不会。内存使用不会随着模块的数量或大小线性扩展。您在 .NET 运行时受到了很大的冲击,但您只受到了一次冲击。您可能会看到一个模块消耗了 40MB,但每个后续模块可能只会再增加几 MB(当然取决于它分配的内存量)。
      • 除了其他人所说的之外,物理内存并不是这里唯一的限制。 Windows 会在它认为合适的时候分页出你的应用程序或其中的一部分,而不一定是在你填满物理内存时。
      【解决方案6】:

      这并不总是与语言有关,通常与语言的使用方式有关。

      好的代码可以在任何语言中有效地使用内存。

      糟糕的代码在每种语言中都会低效地使用内存

      【讨论】:

      • 嗯,只对了一半。在这种情况下,语言/运行时确实确实很重要。 -1
      • 正确,如果您关心几百 K 的内存,C 通常是更好的选择。
      • 我的回答有什么不实之处? (为了强调而略有更新)
      • 我在 99% 的时间里都会打电话给 BS。当你秘书的 PC 有 2+gb 时,谁会在乎你的 LoB 应用程序有一些字典和 UI 元素是否占用 100mb 的 RAM?
      【解决方案7】:

      如果您使用任务管理器查看内存可能会产生误导。工作集是映射到进程的虚拟内存量。不一定你的应用程序使用了多少——在 .NET 的垃圾收集环境中尤其如此。当您的程序分配内存时,.NET CLR/GC 通常会从操作系统请求比实际需要更多的内存,以便将来可以有效地将内存分配给程序中的托管对象。

      查看这是否影响您的一种快速而肮脏的方法(非常肮脏)是将Process.MaxWorkingSet 属性设置为0。这类似于在Win32 中使用SetProcessWorkingSetSize 来尝试修剪映射到过程。如果您立即看到内存使用量下降,那么您就知道发生了什么。但是,一旦您通过 GC/CLR 再次开始分配内存,它就会恢复正常 - 通常这是一件好事。真的,你不应该担心它,让 GC 有机会以正确的方式去做。

      为了优化程序的内存使用并更好地了解 CLR 中内存分配的工作原理,我建议您开始使用 dotTrace(我的偏好)、Ants Profiler(顺便提一下 publishes a cool video on this topic here)。 CLRProfiler 也很有趣,不过现在有点过时了,不过还是免费的。

      【讨论】:

        【解决方案8】:

        进行快速计算,您可能分配了大约 340K 对象(减去了 32 位单控制台应用程序通常的 c12MB 命中,史蒂夫!)并看到 [删除对每个对象吃 8 字节的典型场景的引用 + 然后一些用于其他臃肿的运行时技术版权@Sun.com..

        尽可能尝试并设计值类型/结构。如果您不能,请告诉您的用户不要运行超过 10 个 .NET 应用程序,否则他们的机器会感觉比 C64 慢。如果它让您感觉更好,请尝试 WPF 或 Silverlight,并为几个按钮和华丽的动画感觉 c100MB 的损失。

        (投反对票的感觉)

        【讨论】:

        • 这是一个糟糕的、不准确的建议。 -1
        • 太糟糕了,我不得不说两次。
        • 当然,请确保您跳转到 CLR 执行引擎设计人员博客并阅读有关他们为什么选择退出值类型技术(实际上是数组分配)的条目,以便他们可以避免这种类型的开销 2他们自 2001 年以来实现的应用程序。顺便说一句,在那个团队中,大多数都完成了,跑掉了,谷歌母舰,它一直在铺平道路..
        • 顺便说一句,在 64 位上,这个开销变得更大了 Ed.. 感受它,衡量它,喜欢它,因为您将创建人类历史上最臃肿的应用程序。..
        • -1。请参阅《框架设计指南第 2 版》第 4.2 节中的“在类和结构之间选择”。 Microsoft 建议不要在除少数情况外的所有情况下定义结构。
        猜你喜欢
        • 2011-01-08
        • 1970-01-01
        • 2010-12-09
        • 2015-11-22
        • 1970-01-01
        • 2011-01-05
        • 1970-01-01
        • 2013-06-15
        • 1970-01-01
        相关资源
        最近更新 更多