#c# #.net #memory-leaks #appdomain
#c# #.net #утечки памяти #appdomain
Вопрос:
Я испытываю утечку памяти в контексте с. AppDomains
Я сократил его до следующего:
У меня есть 3 проекта, два библиотечных проекта и консольный проект: общий DynamicallyLoadable
и RemotingTimeoutPrototype
(консольная программа). Общий содержит интерфейсы, используемые обоими DynamicallyLoadable
и RemotingTimeoutPrototype
. Обе ссылки являются общими во время компиляции. Ни в одном из проектов нет других ссылок во время компиляции.
Общий содержит это:
public interface IHostService
{
string GetStuff();
}
public interface IRemoteClass
{
IHostService Alpha { get; set; }
string CallHostServices();
}
Динамически загруженный содержит только один тип:
public class RemoteClass : MarshalByRefObject, IRemoteClass
{
public IHostService Alpha { get; set; }
public string CallHostServices()
{
Console.WriteLine("Domain {0}, RemoteClass.CallHostServices():", AppDomain.CurrentDomain.Id);
return Alpha.GetStuff();
}
}
Консольный проект содержит реализацию IHostService
:
public class Alpha : MarshalByRefObject, IHostService
{
readonly byte[] mBuffer = new byte[100*1024*1024];
public Alpha()
{
for (var i = 0; i < mBuffer.Length; i)
mBuffer[i] = (byte) (i%256);
}
public string GetStuff()
{
return "Alpha";
}
}
Программа.Main состоит из этого:
while (true)
{
var otherDomain = AppDomain.CreateDomain("OtherDomain");
var proxy =
(IRemoteClass)
otherDomain.CreateInstanceFromAndUnwrap("../../../DynamicallyLoadable/bin/debug/DynamicallyLoadable.dll",
"DynamicallyLoadable.RemoteClass");
var alpha = new Alpha();
proxy.Alpha = alpha;
Console.WriteLine(proxy.CallHostServices());
Thread.Sleep(TimeSpan.FromSeconds(2));
AppDomain.Unload(otherDomain);
RemotingServices.Disconnect(alpha); // this was just an attempt, doesn't change a thing whether it's there or not
GC.Collect(); // same here, this shouldn't really be necessary, I just tried it out of despair
}
Теперь, когда я позволяю этому работать некоторое время, потребление памяти постоянно увеличивается, и в конце концов я попадаю в исключение OutOfMemoryException.
Я ожидал бы, что если домен, содержащий прокси, выгружен, то прокси тоже выгружается, и больше нет ссылок на конкретную альфа-версию, поэтому это будет собрано. Но, очевидно, это не так.
Обратите внимание, что я также проверил, что домен действительно выгружается, сославшись на mscoree и перечислив загруженные домены с помощью кода в соответствии со строками:
var runtimeHost = new CorRuntimeHost();
runtimeHost.EnumDomains(out handle);
// etc.
Кроме того, если я присоединю обработчик к otherDomain.DomainUnload(), этот обработчик вызывается просто отлично.
Кто-нибудь может пролить свет на это, пожалуйста?
Ответ №1:
Это может быть типичной проблемой заблокированного потока завершения (поскольку у вас есть цикл while, вероятно, в потоке STA и вы никогда не передаете управление другим потокам явно). Даже если вы выгружаете / удаляете домен, он может находиться в памяти, потому что он не завершен. Подробнее читайте здесь: http://alexatnet.com/articles/memory-leaks-in-net-applications — в частности, раздел «Заблокированный поток завершения» (я автор)
Комментарии:
1. Я только что прочитал вашу статью. Завтра я буду использовать WinDbg в соответствии с вашим описанием (сейчас я живу почти в 2 часа ночи;), Чтобы проверить, вижу ли я какие-либо накопления в объектах «ready for finalization». Интересно, однако, что было бы исправлено в моем случае. Может быть, уместно: цикл while — это то, из чего состоит мой Main, больше ничего.
2. Я просто попробовал более простой способ проверить, уничтожается ли прокси-экземпляр RemoteClass, добавив деструктор в RemoteClass следующим образом: ~RemoteClass() { Консоль. WriteLine(«Уничтожается»); } — и я вижу сообщение консоли, таким образом, прокси-экземпляр уничтожается. Что опровергло бы теорию финализатора, не так ли?
3. Я предполагаю, что это не RemoteClass, который должен быть уничтожен / завершен в этом потоке, это прокси. В любом случае, вы пытались добавить WaitForPendingFinalizers после GC. Собирать?