[英]Why is it taking so long to GC System.Threading.OverlappedData?
我正在通過內存分析器運行我的應用程序來檢查泄漏。 事情似乎有點好,但我得到了很多這些OverlappedData似乎在終結器隊列中徘徊,幾乎沒有任何東西。 它們是通過關閉連接任一端的底層NetworkStream
而取消的重疊IO的結果。
網絡流本身被丟棄。 任何地方都沒有NetworkStream
實時實例。
通常它們的根源是被稱為OverlappedDataCacheLine
的東西。我在回調中調用EndRead
是我做的第一件事,因此沒有調用BeginRead
應該沒有它的相應EndRead
。
這是一個非常典型的外觀,是誰從工具中保留它
最后它確實得到了GC但它需要永遠 - 大約半小時的時間來殺死所有內容,當我開始大約一千個流時,將它們放入異步調用BeginRead
並在大約一分鍾后關閉它們。
該程序在端口80上對網絡服務器稍微重現了這個問題。任何網絡服務器都會真正做到。
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Threading;
class Program
{
static void Main(string[] args)
{
var clients = new List<TcpClient>();
for (int i = 0; i < 1000; ++i) {
var client = new TcpClient();
clients.Add(client);
client.BeginConnect("localhost", 80, connectResult =>
{
client.EndConnect(connectResult);
var stream = client.GetStream();
stream.BeginRead(new byte[1000], 0, 1000, result =>
{
try
{
stream.EndRead(result);
Console.WriteLine("Finished (should not happen)");
}
catch
{
// Expect to find an IO exception here
Console.WriteLine("Faulted");
}
}, stream);
}, client);
}
Thread.Sleep(10000); // Make sure everything has time to connect
foreach (var tcpClient in clients)
{
tcpClient.GetStream().Close();
tcpClient.Close();
}
clients.Clear(); // Make sure the entire list can be GC'd
Thread.Sleep(Timeout.Infinite); // Wait forever. See in profiler to see the overlapped IO take forever to go away
}
}
當然,這個程序並不需要永遠清理OverlappedData
一千個OverlappedData
因為它比真正的應用程序要小,但確實需要一段時間來完成它。 在運行真實的東西而不是這個測試應用程序時,我會收到卡住終結器的警告。 它在我的應用程序中沒有做太多,只是嘗試關閉可能沒有關閉的所有內容,並確保沒有任何引用被保存在任何地方。
如果我在客戶端上調用Dispose()
或Close()
並且它是流,那么它似乎並不重要。 結果是一樣的。
任何線索,為什么會發生這種情況以及如何避免這種情況? CLR對我來說很聰明,並且保留這些固定的內存塊可能會為新的呼叫做准備嗎? 為什么終結者的完成速度如此之慢?
更新在做了一些令人難以置信的愚蠢的負載測試之后,在F5鍵上放一杯水並喝點咖啡,似乎有些東西會在收集這些東西的壓力下觸發更完整的GC。 所以實際上似乎並不是一個真正的問題,但仍然很高興知道這里實際發生了什么,為什么收集這個對象的速度比其他對象慢,如果這可能是一個問題,在后期階段與碎片記憶等。
好吧,現在看來發生了什么。 只有在分配內存時才會發生垃圾收集。 它需要至少2兆字節的分配,即生成0 GC堆的典型初始大小以觸發GC。 換句話說,一個什么都不做的程序永遠不會運行GC,你會看到任何尚未在堆中使用內存分析器收集的對象很長一段時間。
這是對你描述的內容的一個很好的解釋。 終止所有連接后,您的程序不再需要執行任何操作。 因此,如果任何內存不會分配太多,所以不會觸發集合。 如果您的探查器沒有顯示集合,那么您可以使用Perfmon.exe查看它們。 這根本不是問題,只是垃圾收集器如何工作的副作用。
只有在有明確證據表明程序存在失控的資源消耗問題時,才會擔心泄漏。
嘗試從AsyncResult
獲取客戶端以排除任何lambda閉包問題。
TcpClient t = (TcpClient)connectResult.AsyncState;
另外,您不應該在完成處理后調用EndConnect
嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.