设置
.NET为段中的每一代堆(0,1,2,LOH)分配内存,以便在启动时以及在集合之后尝试满足分配请求时获得连续的内存块。
为应用程序“预热”时,为每个堆分配的内存可能会趋于平稳,除了第2代和大对象堆。在垃圾收集期间,每个堆(0,1,2)都被扫描并压缩,除了大扫描的大对象堆(LOH)。
我理解集合的'sweep'部分意味着GC识别哪些对象不再有根并且可用于收集(或完成),而'compact'意味着堆中仍然存在的地址重新组织,以便可用的剩余堆具有更多可用的连续内存。
由于超出了堆中每个段的预算,.NET将分配另一个段以便在可能的情况下完成分配。
问题
我的问题归结为每个堆中的内存会发生什么,不再被应用程序(已提交)使用,但仍由.NET保留? 什么时候发布回操作系统?。
我认为这是一个过程可能会占用大量内存的场景(虚拟大小非常大,但私有字节很小),但在检查其堆时主要是可用空间。另外需要注意的是,堆的总大小也可能非常小,并且不考虑进程消耗的内存。
没有阻止的终结器,并且对于一个进程看起来都很健康 - 它可能在触发监视器警报之前已经运行了几周(例如)。
尝试进一步澄清问题,如果您阅读Tess .NET Memory Management - A Restaurant Analogy,如果表是堆段,餐厅是否会丢失表(例如免费堆段)?
修改
答案 0 :(得分:10)
我的回答是 - 没关系。操作系统为应用程序(运行.NET运行时)提供虚拟内存。这不是“真正的”记忆。操作系统可以将每页虚拟内存放在任何地方 - 在处理器上,主内存中,在磁盘上。因此,应用程序可以使用比系统上的RAM数量更多的内存,并且操作系统将复制磁盘所需的位以确保应用程序保持运行(模块化某些寻址和技术限制)
操作系统管理系统上所有进程的虚拟内存,并确保一个程序不会占用系统上的所有RAM而损害其他程序。当.NET运行时要求系统中的内存用于堆中,但是然后不使用它时,此内存将(如果系统在可用RAM上较低)将被移动到磁盘,因为它没有被访问
通过Tess的电子邮件澄清:(强调我的)
在整个应用过程中,段大小保持不变,但这里有两件事需要考虑。
分段的分配是虚拟分配,这意味着我们保留虚拟内存时,我们只提交我们实际使用,因此用于段的专用字节与段大小不同,这意味着在GC之后,您的专用字节将关闭,而您的虚拟字节将保持不变。
当不再使用某个段时,即如果您发生某个段中的所有内容以使其不再包含任何.net对象,则将虚拟alloc返回给操作系统。强>
根据信仰,然后堆段(餐馆表)将返回操作系统。
答案 1 :(得分:0)
我不知道这个问题的答案,但我怀疑.NET在进程退出之前不会释放它(并且可能在卸载AppDomain时猜测)。这只是基于我观看perfmon .NET内存计数器。 GC总字节数计数器显示,至少在我的应用程序中,保留字节在我的应用程序的生命周期中上下变化,大约30MB。
而且,如果GC在服务器模式下运行,那么.NET可能会更频繁地释放内存。