Der*_*all 15 c# garbage-collection memory-leaks asyncsocket
我正在通过内存分析器运行我的应用程序来检查泄漏.事情似乎有点好,但我得到了很多这些OverlappedData似乎在终结器队列中徘徊,几乎没有任何东西.它们是通过关闭NetworkStream连接两端的底层而取消的重叠IO的结果.
网络流本身被丢弃.没有NetworkStream任何地方的实时实例.
通常,他们根植于一些被称为OverlappedDataCacheLine.I'm调用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
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这个程序不需要永远清理千元,OverlappedData因为它比实际的应用程序小,但确实需要一段时间才能完成它.在运行真实的东西而不是这个测试应用程序时,我会收到卡住终结器的警告.它在我的应用程序中没有做太多,只是尝试关闭可能没有关闭的所有内容,并确保没有任何引用被保存在任何地方.
如果我打电话Dispose()或Close()在客户端上,它似乎并不重要.结果是一样的.
任何线索,为什么会发生这种情况以及如何避免这种情况?CLR对我来说很聪明,并且保留这些固定的内存块可能会为新的呼叫做准备吗?为什么终结者的完成速度如此之慢?
更新通过戴上F5键一杯水,并得到一些咖啡做了一些令人难以置信的愚蠢的负载测试后,它似乎是什么触发压力下,收集这些东西更完整的GC.所以实际上似乎并不是一个真正的问题,但仍然很高兴知道这里实际发生了什么,为什么收集这个对象的速度比其他对象慢,如果这可能是一个问题,在后期阶段与碎片记忆等.
好吧,现在看来发生了什么.只有在分配内存时才会发生垃圾收集.它需要至少2兆字节的分配,即生成0 GC堆的典型初始大小以触发GC.换句话说,一个什么都不做的程序永远不会运行GC,你会看到任何尚未在堆中使用内存分析器收集的对象很长一段时间.
这是对你描述的内容的一个很好的解释.终止所有连接后,您的程序不再需要执行任何操作.因此,如果任何内存不会分配太多,所以不会触发集合.如果您的探查器没有显示集合,那么您可以使用Perfmon.exe查看它们.这根本不是问题,只是垃圾收集器如何工作的副作用.
只有在有明确证据表明程序存在失控的资源消耗问题时,才会担心泄漏.