我们正在使用.NET运行Web farm。每个Web服务器在其内存中保存了相当数量的静态对象。第二代垃圾回收(GC)需要10-20秒,并且每5分钟运行一次。我们遇到了与StackOverflow类似的问题:http://samsaffron.com/archive/2011/10/28/in-managed-code-we-trust-our-recent-battles-with-the-net-garbage-collector
目前,我们正在减少缓存中对象的数量。然而,这需要时间。
同时,我们实现了文档here中记录的方法,在.NET中获取有关接近GC的通知。目标是在GC即将到来时将Web服务器从farm中取出,并在GC结束后将其包含在farm中。但是,我们仅收到0.7%的所有GC的通知。我们使用了8个maxGenerationThreshold和largeObjectHeapThreshold。我们尝试了其他阈值,但错过的GC数量没有改变。
我们使用并发服务器垃圾回收(http://msdn.microsoft.com/en-us/library/ms229357.aspx)。GCLatencyMode为Interactive(见http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx)。在这里,我们尝试使用其他GC模式(Workstation mode、Batch等)。但是,我们大部分的GC都没有得到通知。
我们做错了什么,或者不可能获得每个GC的通知?如何增加通知数量?
根据http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdf,一开始当Gen2达到约10MB时会触发GC。我们有很多RAM,因此,如果我们可以将此阈值手动设置为更高的级别,那么到达此阈值所需的时间将更长,我的理解是获得通知的概率会增加。是否有一种方法可以修改此阈值?
以下是注册和侦听通知的代码:
同时,我们实现了文档here中记录的方法,在.NET中获取有关接近GC的通知。目标是在GC即将到来时将Web服务器从farm中取出,并在GC结束后将其包含在farm中。但是,我们仅收到0.7%的所有GC的通知。我们使用了8个maxGenerationThreshold和largeObjectHeapThreshold。我们尝试了其他阈值,但错过的GC数量没有改变。
我们使用并发服务器垃圾回收(http://msdn.microsoft.com/en-us/library/ms229357.aspx)。GCLatencyMode为Interactive(见http://msdn.microsoft.com/en-us/library/system.runtime.gclatencymode.aspx)。在这里,我们尝试使用其他GC模式(Workstation mode、Batch等)。但是,我们大部分的GC都没有得到通知。
我们做错了什么,或者不可能获得每个GC的通知?如何增加通知数量?
根据http://assets.red-gate.com/community/books/assets/Under_the_Hood_of_.NET_Management.pdf,一开始当Gen2达到约10MB时会触发GC。我们有很多RAM,因此,如果我们可以将此阈值手动设置为更高的级别,那么到达此阈值所需的时间将更长,我的理解是获得通知的概率会增加。是否有一种方法可以修改此阈值?
以下是注册和侦听通知的代码:
GC.RegisterForFullGCNotification(gcThreshold, gcThreshold);
// Start a thread using WaitForFullGCProc.
thWaitForFullGC = new Thread(WaitForFullGCProc);
thWaitForFullGC.Name = "HealthTestGCNotificationListenerThread (Threshold=" + gcThreshold + ")";
thWaitForFullGC.IsBackground = true;
WaitForFullGCProc():
private void WaitForFullGCProc()
{
try
{
while (!gcAbort)
{
// Check for a notification of an approaching collection.
GCNotificationStatus s;
do
{
int timeOut = CheckForMissedGc() > 0 ? 5000 : (10 * 60 * 1000);
s = GC.WaitForFullGCApproach(timeOut);
if (this.GcState == GCState.InducedUnnotified)
{
// Set the GcState back to okay to prevent the message from staying in the ApplicationMonitoring.
this.GcState = GCState.Okay;
}
} while (s == GCNotificationStatus.Timeout);
if (s == GCNotificationStatus.Succeeded)
{
SetGcState(GCState.Approaching, "GC is approaching..");
gcApproachNotificationCount++;
}
else
{
...
}
Stopwatch stopwatch = Stopwatch.StartNew();
s = GC.WaitForFullGCComplete((int)PrewarnTime.TotalMilliseconds);
long elapsed = stopwatch.ElapsedMilliseconds;
if (s == GCNotificationStatus.Timeout)
{
if (this.ForceGCWhenApproaching && !this.IsInGc && !this.IsPeriodicGcApproaching)
{
this.IsInGc = true;
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true);
GC.WaitForPendingFinalizers();
elapsed = stopwatch.ElapsedMilliseconds;
this.IsInGc = false;
}
}
}
gcAbort = false;
}
catch (Exception e)
{
}
}