Создание утечки памяти в C # или VB.Net - PullRequest
4 голосов
/ 02 июля 2011

Вдохновленный этим вопросом , мне было интересно, каковы возможные способы создания утечки памяти в .Net. Однажды я нашел один с ODBC Data Access. Кто-нибудь имел опыт работы с последней версией?

Редактировать: Есть ли какие-нибудь нормальные, безвредные способы использования, которые могут вызвать утечки памяти?

Ответы [ 6 ]

4 голосов
/ 02 июля 2011

Самый простой способ создать утечку памяти - это неправильно использовать средства, предназначенные для взаимодействия, поскольку они имеют дело с неуправляемой памятью.

Например, выделите GCHandle, указывающий на объект, и никогда не освобождайте его.

Редактировать: Есть ли какие-то нормальные, безвредные способы использования, которые могут вызвать утечки памяти?

Только один, о котором я знаю, особенно в некоторых программах с пользовательским интерфейсом. Это было возможно в WinForms, но только недавно стало распространенным благодаря WPF с MVVM:

Ключевым моментом, который многие упускают из виду, является то, что делегат содержит ссылку на объект, над которым он работает.

Таким образом, если используется шаблон, такой как MVVM, с двусторонними привязками (реализованный с использованием событий, состоящих из делегатов), и если представление изменяется на другое представление с той же моделью представления, по умолчанию обновления модели представления остаются привязан к обоим представлениям, что приводит к утечке старого представления.

Теоретически это может произойти с любым делегатом, но на практике это не распространено, поскольку подписчик, как правило, переживает издателя или отписывается.

Аналогичная ситуация возникает, когда отписываются обработчики лямбда-событий:

timer.Elapsed += (_, __) => myObj.Notify();
timer.Elapsed -= (_, __) => myObj.Notify();

В этом случае, хотя лямбда-выражения идентичны, они представляют разные обработчики, поэтому событие Elapsed все равно вызовет Notify. Вторая строка выше не имеет никакого эффекта; не отписывается и не выдает ошибку.

Обратите внимание, что неправильные отписки обычно обнаруживаются во время тестирования, поэтому они редко вызывают "утечки" в выпущенном коде. Напротив, описанная выше ситуация с MVVM не вызывает видимых побочных эффектов (кроме утечки памяти и ресурсов).

2 голосов
/ 02 июля 2011

GCHandle.Alloc() - это прекрасный способ создать «настоящие» утечки памяти в .NET.(«истинная» утечка, как в полностью недоступна недоступна без хаков / отражений, но все еще протекает)

РЕДАКТИРОВАТЬ

Редактировать : Существуют ли какие-либо нормальные, безвредные способы использования, которые могут вызвать утечку памяти?

«Кажется нормальным» зависит от ваших знаний / опыта.

Например System.Windows.Forms.Timer "корни "сам, ​​пока он включен (на самом деле с помощью GCHandle.Alloc()).Если вы добавите Timer к Form через графический редактор Visual Studio, VS

  • добавит коллекцию "компонентов" в ваш класс
  • сгенерирует код, который добавляет Timer в эту коллекцию «компонентов»
  • генерирует код, который располагает все в коллекции «компонентов» в методе Dispose() формы

Это означает, что все будет работать, как ожидалось, нетутечка.

Но если вы добавите код, который создает и запускает Timer самостоятельно, легко забыть добавить код, который останавливает / удаляет его.И Visual Studio не сделает (не сможет) сделать это за вас.

В этом случае Timer останется в живых. никогда не будет .И он будет продолжать работать (и запускать события).Даже если Form закрыт / удален.

И, поскольку вы обычно подключаете событие Timer s Tick к некоторой функции-члену Form, Form также будетоставайся в живых.(Timer является корнем, Timer ссылается на делегата события, ссылки на делегата события Form.)

Поскольку все еще есть много людей, которые не знают или не заботятся о подобных вещах,код будет выглядеть для них довольно "нормально".

1 голос
/ 02 июля 2011

Один из примеров использования - это ситуация, когда у класса есть личная коллекция, из которой никто никогда не удаляет, но кто-то продолжает добавлять объекты.

Скажем, есть фоновый поток, который должен обрабатывать элементы изСтатическая блокировка коллекции и поток умирает, или блокирует.Тогда коллекция может только расти, вызывая утечку:

public class Test
{
    static Test()
    {
        Task.Factory.StartNew(() =>
        {
            Random r = new Random();

            try
            {
                while (true)
                {
                    object o = col.Take();

                    //process o fails at some point
                    if (r.Next(100) == 0)
                    {
                        Console.WriteLine("Fail! No one is processing anymore.");
                        throw new Exception();
                    }
                }
            }
            catch
            {
                Console.WriteLine("We caught the exception, but didn't resume processing");
            }
        });
    }

    private static BlockingCollection<object> col = new BlockingCollection<object>();

    public void Add(object o)
    {
        col.Add(o);
    }
}

class Program {
    public static void Main(string[] args)
    {
        Test t = new Test();
        while (true)
            t.Add(new object());
    }
}
1 голос
/ 02 июля 2011

Злоупотребление Финализатором может привести к «утечке памяти», то есть это приведет к созданию объекта, память которого никогда не может быть запрошена GC: * ​​1001 *

public class Foo
{
    int[] value = new int[100];

    ~Foo()
    {
        GC.ReRegisterForFinalize(this);
    }
}
1 голос
/ 02 июля 2011

Если вы прочитаете все ответы, приведенные в вашей ссылке, они в значительной степени относятся и к .Net. Хотя CLR и JVM - это совершенно разные системы, они по-прежнему весьма схожи по своей философии проектирования (в частности, обе они являются управляемыми системами), поэтому у них много общих достоинств и недостатков.

0 голосов
/ 02 июля 2011
for (;;)
    Marshal.AllocHGlobal(0x400);
...