Я запускаю свое приложение через профилировщик памяти для проверки утечек. Кажется, что все в порядке, но я получаю много этих OverlappedData, которые, кажется, висят вокруг в очереди финализатора, практически ничего не делая. Они являются результатом перекрытия IO, который был отменен путем отключения базового NetworkStream
на обоих концах соединения.
Сам сетевой поток. В настоящее время нет живых экземпляров NetworkStream
.
Обычно они внедряются во что-то, что называется OverlappedDataCacheLine
. Я вызываю EndRead
в обратном вызове, первое, что я делаю, поэтому вызов BeginRead
не должен иметь соответствующий EndRead
.
Это довольно типичный вид, который удерживает его от инструмента
В конце концов, он получает GC'd, но он берет навсегда - в порядке получаса, чтобы убить все, когда я начал около тысячи потоков, поместил их в асинхронный вызов 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
, так как она меньше, чем реальное приложение, но для выполнения этой задачи требуется некоторое время. Я получаю предупреждения за застрявший финализатор при запуске моих реальных вещей вместо этого тестового приложения. Это мало что делает в моем приложении, просто пытается закрыть все, что не могло быть закрыто, и гарантирует, что ссылки нигде не будут сохранены.
Это вообще не имеет значения, если я назову Dispose()
или Close()
на клиенте и потоке. Результат тот же.
Любая подсказка о том, почему это происходит и как этого избежать? Это CLR умнее на мне и, возможно, сохранить эти блокированные блоки памяти в состоянии подготовки к новым вызовам? И почему финализатор настолько невероятно медленно завершает работу?
Обновить После выполнения невероятно тупых нагрузочных тестов, поставив стакан воды на ключ F5 и получив кофе, кажется, что что-то вызывает более полный GC при стрессе, который собирает эти вещи. Таким образом, на самом деле нет реальной проблемы, но все же было бы неплохо узнать, что на самом деле происходит здесь, и почему сбор этого объекта является величиной медленнее, чем другие объекты, и если это потенциально может быть проблемой на более позднем этапе с фрагментированным памяти и т.д.