我的程序分配了大量包含长寿命DAWG的实例。在构建这个DAWG的过程中,有时候进度会减慢100倍,这与.NET执行gen 2 GC集合完全相关。在这些期间,“GC中的%时间”为99.5%,“总第2代收集”每隔几秒递增一次。在几个背靠背第2代集合之后,它们停止触发没有明显的原因,程序再次加速。几分钟后,循环重新开始。
我创建的实例数量大约为2500万,并且它们占用了几GB的RAM,所以第2代集合花费这么长时间并不太令人惊讶。令人惊讶的是,第二代收藏品来自“火车”,并不断被触发。
如果不重新考虑我的方法,我能以某种方式阻止这种情况吗?也许有一些方法可以让.NET暂停第2代收藏,直到另行通知?除了这些事件之外,程序运行非常有效,因此除了这种不幸的角落行为之外,.NET显然完全可以完成这项任务。
(我尝试将GCSettings.LatencyMode设置为GCLatencyMode.Batch,但问题仍然存在.GC开始发生时的可用物理RAM量大约为1GB。这是在64位计算机上。)
答案 0 :(得分:4)
在.NET 4.5+中,您可以使用新的GCLatencyMode
选项指定您对较少Gen 2集合的首选项。
GCSettings.LatencyMode = GCLatencyMode.SustainedLowLatency;
更多信息:
我的应用无法容忍某个时间窗口内的暂停
越来越多的.NET开发人员构建了商业应用程序和 根据定义的业务提供结果的服务 要求或SLA。股票市场是必须的服务的例子 在市场开放的同时提供非常及时的结果。通常,这些 应用程序在他们想要的时间内执行重要的工作 提供低延迟的结果。但他们不能容忍明显的 由于收集暂停。
我们的客户告诉我们他们会在他们身上部署更多内存 服务器如果这样做会消除长暂停时间(通常是这样) 完全阻止GCs介绍)。在.NET Framework 4.5中,我们 通过引入SustainedLowLatency模式提供该选项,其中 避免完全阻止GC。此模式也适用于 .NET Framework 4中的工作站GC通过Update 4.0.3。
当SustainedLowLatency设置生效时,第0代, 第1代,后台第2代收藏仍然发生 通常不会导致明显的暂停时间。阻塞一代2 仅当机器内存不足或应用程序时才会收集 通过调用GC.Collect()来引发GC。部署至关重要 将SustainedLowLatency设置用于具有的机器上的应用程序 足够的内存,因此它们将满足堆中的最终增长 设置生效时。
在.NET Framework 4.5中,可以使用SustainedLowLatency模式 工作站和服务器GC。要打开它,请设置 GCSettings.LatencyMode属性为GCLatencyMode.SustainedLowLatency。 .NET Framework 4包含用于工作站GC的LowLatency模式; 但是,此设置仅用于短时间 时间,而SustainedLowLatency模式旨在用于很多 更长的时间。
您还可以尝试NoGCRegion
。
https://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode%28v=vs.110%29.aspx
表示应用程序暂停垃圾收集 执行关键路径。 NoGCRegion是一个只读值;那是, 您无法将NoGCRegion值分配给GCSettings.LatencyMode 属性。您可以通过调用来指定无GC区域延迟模式 TryStartNoGCRegion方法并通过调用来终止它 EndNoGCRegion方法。
答案 1 :(得分:3)
从.NET 4.6开始,您可以使用GC.TryStartNoGCRegion和GC.EndNoGCRegion方法“尝试禁止”代码区域中的垃圾回收。
try
{
GC.TryStartNoGCRegion(TOTAL_SIZE, true);
<No GC region code here>
}
finally
{
if (GCSettings.LatencyMode == GCLatencyMode.NoGCRegion) GC.EndNoGCRegion();
}
有关参数和可能的异常的更多信息,请参阅here。
但是,仍无法保证不会调用GC。例如,如果您在“无GC区域代码”中使用某个库并且该库调用GC.Collect(),则不仅会发生垃圾收集,而且GC.EndNoGCRegion()也会抛出异常。
我担心你不得不重新考虑你的方法,因为似乎没有构建.NET垃圾收集(从.NET 4.6开始)来处理数百万个具有复杂关系的分配对象。这里的帖子中提到的NFX库是一个选项,也是使用结构,简化模型以减少关系,使用索引到数组而不是引用等。
答案 2 :(得分:1)
我们遇到了缓存社交+路由数据的类似问题。 我们不得不缓存数以亿计的条目,因为由于网络流量(即使在本地主机上),存储这个进程外的速度还不够快。 相反,我们创建了一个特殊的100%托管内存管理器,它分配byte []段,子分配那里的空间。来自CLR堆的对象变为&#34; PilePointers {int segment,int address}&#34;通过特殊的二进制序列化,比BinaryFormatter快几个数量级。现在,可以在托管堆中为LOMG TIME存储任何复杂性和大小的对象图,但GC阻塞暂停&lt; 10 ms,完整GC约为30-60 ms。
我们在64Gb PC上轻松存储 300,000,000个对象,最有趣的部分是:解决方案比在非托管堆中存储对象更快通过Marshal或通过redis / memcache进行进程外。
看到这个: 桩: https://www.youtube.com/watch?v=IUBF2Ncvbbs
高速缓存: https://www.youtube.com/watch?v=Dz_7hukyejQ
获取代码(Apache 2): https://github.com/aumcode/nfx
答案 3 :(得分:-2)
你无法抑制它,因为必要 保证程序的正确性(至少就框架而言)。
简单地说,CLR不会让你危险地生活。如果您需要手动内存管理,那么您必须从struct
生成自己的数据结构,并手动管理所有内容。
也就是说,2500万是很多对象,但它不应该是几千兆字节,除非你的对象也相对较大。你可以使用结构数组而不是类来避免额外的引用吗?您可以删除任何信息吗?