Утечка памяти для службы, переданной в выгруженный домен приложения

#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. Собирать?