.NET应用程序本质上是内存密集型的吗?

5
我第一次用C#编写一个大型应用程序。我编写了一个示例模块来测试我的软件背后的想法。这个模块包含了几十个C#字典和对象列表,每个对象都有几个成员和属性。
令人震惊的是,在初始化核心对象之后,它使用了约40MB的RAM。
我进行了测试,并发现在对象初始化后分配了超过30MB的内存,但是我认为,考虑到我的对象大小,不应该消耗超过几百KB的内存。
我做错了什么,还是.NET相比本地代码应用程序自然具有内存密集性?

2
我同意你的观点。我认为你在做某些事情上犯了严重的错误。 - Sergey
你能发布一些示例代码吗?我认为这将极大地帮助理解是代码单独引起的问题还是由运行时复合引起的问题。 - Abhijeet Patel
@Jed,重量是一个相对的术语。今天普通PC有多少RAM? - Chris Ballance
8个回答

12

你是如何确定使用了多少内存的?.NET应用程序被认为会比需要更积极地保留更多的内存,只要有足够的内存可用,并且在内存不足时释放内存回系统。

我认为你可以在这篇MSDN文章中获得一些指针。


通过使用任务管理器 - 感谢指出它可能不是一个有效的数字 - PiotrK

7
使用任务管理器查看内存使用情况很可能会非常不准确。相反,获取一款适当的内存分析工具(dotTrace非常好用,而且有10天试用期),并花时间查看你实际的内存消耗。
我在自己的代码中看到的一些问题包括:
- 低估了我实际使用的内存量(没有正确计算单独的对象,没有考虑到列表可能有“多余”的容量) - 保留了我不需要的短暂对象的引用 - 没有考虑到操作期间创建的短暂对象尚未被垃圾回收 - 没有考虑到未分配的内存 - 已从操作系统中索取的内存(因此在任务管理器中显示为进程的一部分),但尚未分配给任何一个对象 - 没有考虑到线程堆栈(每个线程都有自己的堆栈;.NET应用程序始终是多线程的,因为框架可能会创建多个线程)。

5
在我们的机器上有几个GB的RAM的日子里,像你通常会用C#构建的用户界面应用程序只使用几百K的RAM的想法是错误的。任何未使用的RAM都是浪费的。
考虑到这一点,C#使用与C++非常不同的内存分配策略,分配和释放较大的块较少。
话虽如此,40似乎有点高。我习惯于非常简单的控制台应用程序接近10-14MB。也许有一个占用内存的表格,或者我使用的是不同版本的框架(2.0)。

4
系统中未使用的RAM是浪费的。如果我的应用程序没有占用它,其他应用程序或操作系统可能会更好地利用它。 - Tom Juergens
2
但它并没有被“独占”,而是由运行时以贪婪的方式分配。如果需要,运行时也可以将其返回给操作系统。 - Ed S.
Linux性能比Windows好的主要原因之一是它将所有未使用的内存用作磁盘缓存,大大提高了读取速度(这非常必要,因为*nix惯用法使用空文件作为标志)。如果您的应用程序占用了这些内存,就无法做到这一点。 - rmeador
Windows也有预取/缓存策略。也许性质不同,但它肯定利用了多余的RAM。例如,Windows 7目前正在使用我的2GB RAM,像Visual Studio这样的应用程序可以立即加载。 - JulianR

2

重点不在于语言,而在于使用方式。

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

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


4
只有一半正确。在这种情况下,语言/运行时确实非常重要。-1 - Ed S.
1
如果你关心几百K的内存,C通常是更好的选择。 - Chris Ballance
1
我的回答哪里不正确?(为了强调稍作修改) - Chris Ballance
2
99%的情况下,我认为这并不重要。当你秘书的电脑有2GB以上的内存时,你的带有一些字典和UI元素的LoB应用程序占用100MB的内存又有什么关系呢? - Rex M

2

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


2

我认为,除非你有非常好的理由担心内存使用情况,否则不要这么做。就像优化一样,只有在客户/最终用户需要进行优化时才应该进行优化。我认为现在用户对内存的限制并不那么严格(除非你在谈论嵌入式系统),他们并不会注意到或关心缺少38 MB的内存。


如果我写了一个单一的模块,使用了40兆字节的内存,而应用程序将有20-30个这样的模块,并且要乘以完整的数据集,那么我担心512兆字节的RAM可能不够用。 - PiotrK
1
@PiotrK:是的,如果那么简单就好了,但事实并非如此。.NET应用程序由运行时管理。使用的内存量并不直接反映您在代码中所做的操作,您的假设是错误的。 - Ed S.
PiotrK: 但你不会这样。内存使用量并不随模块数量或大小呈线性比例增长。你需要为.NET运行时付出很大的代价,但你只需要承受一次。你可能会发现,一个模块会消耗40MB,但每个后续模块可能只会增加几MB(当然这取决于它分配了多少内存)。 - itowlson
除了其他人已经说过的,物理内存并不是这里唯一的限制。Windows会根据需要将您的应用程序或其部分页面化,而不一定是当您填满物理内存时。 - jasonh

0
如果您使用任务管理器查看内存,则可能会导致误解。工作集是映射到进程的虚拟内存量,不一定是应用程序实际使用的内存量 - 在.NET的垃圾回收环境中尤其如此。随着程序分配内存,.NET CLR / GC通常会从操作系统请求比实际需要更多的内存,以便将来可以将该内存高效地分配给程序中的受管对象。
一个快速而且很“肮脏”的方法,用于查看这是否对您产生影响,就是将Process.MaxWorkingSet属性设置为0。这类似于在Win32中使用SetProcessWorkingSetSize尝试修剪映射到进程的页面数量。如果您立即看到内存使用量下降,则知道发生了什么。但是,一旦您再次通过GC / CLR分配内存,它将会上升 - 通常这是好事。真正应该关注的是,给GC机会以正确的方式处理它。
为了优化程序的内存使用并更好地了解CLR中的内存分配工作原理,我建议您开始尝试dotTrace(我的首选),Ants Profiler(顺便说一下,他们在此主题上发布了一个很棒的视频here)。CLRProfiler也很有趣,但这些天它有点过时了,而且是免费的。

-8

快速计算一下,您可能正在分配约340K个对象(减去32位单控制台应用程序的通常c12MB命中,哎呀Steve!),并且由于臃肿的运行时技术版权@ Sun.com,引用类型的运行时和System.Object浪费[删除对典型场景每个对象消耗8字节的引用+其他常见嫌疑人的参考]。

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

(踩感)


2
这是糟糕的,不准确的建议。-1 - Ed S.
4
太糟糕了,我不得不说两次。 - Ed S.
当然,确保你跳转到CLR执行引擎设计师的博客,并阅读有关他们选择值类型技术的条目,实际上是数组分配,以便他们可以避免自2001年以来实现的两个应用程序中出现这种开销。顺便说一句,在那个团队中,大多数人都已经完成了任务,逃离了谷歌母舰,而它仍在不断铺平道路... - rama-jka toti
1
请参阅《框架设计准则第二版》第4.2节中的“选择类和结构体”一章。微软建议仅在少数情况下定义结构体,而不是所有情况下都定义。 - TrueWill
去读一遍那本书,特别是他们反对接口而更支持抽象类的部分。顺便说一下,运行FXCop并确保它满足Abrams设计,这导致了引用类型的大量增加,这正是CLR执行团队努力击败Java并在其自己的JVM实现中击败它的问题,然后Sun垃圾开始了...找一个更好的参考资料,认真地...它很好,复制了Java元素的风格炒作...等等。我的建议是,做你自己,读懂字里行间,超越Redmond。 - rama-jka toti
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接