Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > .NET для новичков > А бывают ли утечки памяти?


Автор: Dims 11.11.2008, 05:09
А бывают ли в Си# утечки памяти? Зачем нужен IDisposable? Часто ли он встречается? Если забыть Dispose, что будет? Как проконтролировать?

Автор: Partizan 11.11.2008, 09:59
Конечно утечки бывают...бажный код -> висячие ссылки на объекты -> сборщик мусора не убивает их...в итоге получаем утечку...

А насчёт IDisposable в MSDN всё предельно ясно написано...

Цитата

 The primary use of this interface is to release unmanaged resources. The garbage collector automatically releases the memory allocated to a managed object when that object is no longer used. However, it is not possible to predict when garbage collection will occur. Furthermore, the garbage collector has no knowledge of unmanaged resources such as window handles, or open files and streams.

Use the Dispose method of this interface to explicitly release unmanaged resources in conjunction with the garbage collector. The consumer of an object can call this method when the object is no longer needed. 


Вопроса "Часто ли он встречается" я вообще не понял...вы о чём?

Ответ на последние два вопроса лежит в приведённой выше цитате из MSDN

Автор: PashaPash 11.11.2008, 11:32
Dims, еще можно добавить невыгружаемые генерируемые сборки и утечки дескрипторов.
Частота встречания зависит от опыта. Например, у класса XmlSerializer 9 конструкторов, из них 7 сильно текут. Это описано в документации, но кто ее читает...

Автор: Dims 11.11.2008, 12:49
Цитата(Partizan @  11.11.2008,  09:59 Найти цитируемый пост)
бажный код -> висячие ссылки на объекты

Это как? Можно поподробнее?

Цитата(Partizan @  11.11.2008,  09:59 Найти цитируемый пост)
А насчёт IDisposable в MSDN всё предельно ясно написано...

Я не врубаюсь. Является ли это обязательным или это просто желательно?

Цитата(PashaPash @  11.11.2008,  11:32 Найти цитируемый пост)
невыгружаемые генерируемые сборки и утечки дескрипторов.

Тоже, если можно, поподробнее smile

Автор: Partizan 11.11.2008, 13:06
Dims, 

Цитата

Я не врубаюсь. Является ли это обязательным или это просто желательно?


Обязательным это не является. Использовать IDisposable необходимо, если ваш класс занимает unmanaged-ресурсы и при удалении объекта их необходимо освобождать. GС при удалении объекта IDisposable обязательно вызовет Dispose.

Цитата

Это как? Можно поподробнее?


Ну, допустим у вас есть класс-коллекция с собственной имплементацией добавления / удаления объектов...
Допустим реализация удаления немного бажная....и иногда объект из коллекции не удаляется, а новые объекты  всё так же добавляются...
В результате объекты, которые по сути нигде не используются остаются торчать в коллекции и GC их на тот свет никак не хочет забирать...
В итоге получаем о-го-гошное отжирание памяти...

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

Автор: PashaPash 11.11.2008, 14:19
Цитата(Dims @  11.11.2008,  12:49 Найти цитируемый пост)
Тоже, если можно, поподробнее 

Код

using System;
using System.Runtime.InteropServices;
using System.Xml.Serialization;

namespace LeakSamples
{
    public struct TestSerializableClass
    {
        int memoryToLeak;
        int memoryToLeak2;
        int memoryToLeak3;
        int memoryToLeak4;
    }

    class Program
    {
        private static void LeakSample1()
        { 
            XmlRootAttribute rootAttr = new XmlRootAttribute("someRoot");
            XmlSerializer serializer = new XmlSerializer(typeof(TestSerializableClass), rootAttr);
        }

        private static void LeakSample2()
        {
            var objectToPin = new TestSerializableClass();
            var handle = GCHandle.Alloc(objectToPin, GCHandleType.Pinned);
            // pass handle.ToIntPtr to some unmanaged code
            // do not call handle.Free();
        }

        static void Main(string[] args)
        {
            Console.WriteLine("Before leak 1: {0}", GC.GetTotalMemory(true));
            for (int i = 0; i < 30; i++)
            {
                LeakSample1();
            }
            Console.WriteLine("After leak 1: {0}", GC.GetTotalMemory(true));

            for (int i = 0; i < 100000; i++)
            {
                LeakSample2();
            }
            Console.WriteLine("After leak 2: {0}", GC.GetTotalMemory(true));
        }
    }
}

LeakSample1 каждый раз создает динамическую сборку и не выгружает ее. И к тому же тормозит. Причины и последствия описаны в документации. 
LeakSample2 закрепляет объект в памяти перед передачей неуправляемому коду. И забывает открепить - не обязательно намеренно, может быть просто из-за исключения в неуправляемом коде. В результате в памяти образуется кусок, который GC не может освободить, и который ему приходится обходить при сборке мусора.

Но чаще всего утечки - это просто висящие ссылки, часто неявные. Например, нестатические обработчики событий и callback-и. Но такие случаи можно отловить профайлером.

Автор: Dims 11.11.2008, 22:55
Цитата(Partizan @  11.11.2008,  13:06 Найти цитируемый пост)
Допустим реализация удаления немного бажная....и иногда объект из коллекции не удаляется, а новые объекты  всё так же добавляются...

Ну это не утечка памяти, а просто я сам не удаляю объекты. Откуда компьютер может знать, что они не нужны?


Цитата(PashaPash @  11.11.2008,  14:19 Найти цитируемый пост)
Но такие случаи можно отловить профайлером. 

Кстати, а что используется в качестве профайлера?

Автор: Partizan 12.11.2008, 09:24
Цитата

Ну это не утечка памяти, а просто я сам не удаляю объекты. Откуда компьютер может знать, что они не нужны?


Вы и сами не знаете, что объекты не удаляете...вы думаете, что удаляете, а из-за багов в коде, объекты не удаляются фактически, потому-что ссылки остаются висеть...Я же сказал, что пример немного надуман, но демонстрирует проблему висящих ссылок.

Есть более удачный пример...

Код

...ThreadStart ts = new ThreadStart(leaky);
   Thread t = new Thread(t);
   t.Start();
...


void leaky()
{
     Thread.CurrentThread.Join();
}



Цитата

Кстати, а что используется в качестве профайлера? 


http://www.google.ru/search?hl=ru&newwindow=1&client=firefox-a&rls=org.mozilla%3Aru%3Aofficial&hs=cgo&q=.net+profiler&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&lr=&aq=f&oq=

Автор: PashaPash 12.11.2008, 10:39
Цитата(Dims @  11.11.2008,  22:55 Найти цитируемый пост)
Кстати, а что используется в качестве профайлера? 

memprofiler.com и стандартный sos.
Цитата(Dims @  11.11.2008,  22:55 Найти цитируемый пост)
Откуда компьютер может знать, что они не нужны?

Весь бонус сборщика мусора в том, что он сам знает какие объекты нужны. Возьми рихтера, там подробно расписан процесс.

Автор: mr.DUDA 12.11.2008, 12:15
Dims, про память в дотнет на основе сборки мусора и поколений отлично у Джеффри Рихтера в книге расписано. smile 

Автор: nerezus 12.11.2008, 16:26
Утечек в pure MSIL не быввает.
Бывают ошибки, когда уже ненужные переменные "как будто нужны и используются", но это уже ошибки проектирования.

Автор: PashaPash 12.11.2008, 17:11
nerezus, тогда и в unmanaged коде утечек памяти не бывает. Просто неосвобожденная память "как будто нужна и используется" - обычная ошибка проектирования.

Автор: nerezus 12.11.2008, 17:40
PashaPash, мы можем присвоить ссылке другое значение в unmanaged коде, но объект в оперативке то остается. Это утечка.

Автор: Partizan 12.11.2008, 17:56
nerezus, 

Код

...ThreadStart ts = new ThreadStart(leaky);
   Thread t = new Thread(ts);
   t.Start();
   t = new Thread(ts);
...
void leaky()
{
     Thread.CurrentThread.Join();
}



Переменная t ссылается на новый объект, старый объект остался...

Автор: PashaPash 12.11.2008, 20:13
nerezus, в примере LeakSample2 выше ссылки на управляемый объект objectToPin уже нет, но он остается в оперативке и GC его обходит стороной. Есть только отметка "этот кусок памяти занят" - ну так она и в unmanaged коде есть. Это тоже утечка. В чистом MSIL.

Просто в managed понятие утечки шире - это когда объект живет дольше, чем должен по представлению разработчика.

Автор: mr.DUDA 13.11.2008, 11:23
Цитата(Partizan @  12.11.2008,  17:56 Найти цитируемый пост)
Переменная t ссылается на новый объект, старый объект остался...

Старый поток вместе со старым объектом Thread подгребётся на следующей же сборке мусора  smile 

Автор: elbjarn 13.11.2008, 12:08
банальность:
вызовите метод Close() в обработчике OnLoad у формы - получите утечкуsmile)

Автор: Partizan 13.11.2008, 12:45
mr.DUDA, как старый поток подгребётся, если он ещё выполняется?  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)