Утечка памяти в C#

Сложно избавиться от всех ссылок, поскольку вам нужно угадать, если такие вызовы:

var workbook = excel.Workbooks.Open("")

Создает экземпляр Workbooks, на который вы не ссылаетесь.

Даже такие ссылки, как:

targetRange.Columns.AutoFit()

Создают экземпляр .Columns(), если вы не знаете и не освобождены должным образом.

В итоге я написал класс, содержащий список ссылок на объекты, которые могли бы распоряжаться всеми объектами в обратном порядке.

Класс имеет список объектов и Add() функций для всего, что вы ссылаетесь при использовании Excel interop, который возвращает сам объект:

    public List<Object> _interopObjectList = new List<Object>();

    public Excel.Application add(Excel.Application obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Range add(Excel.Range obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Workbook add(Excel.Workbook obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheet add(Excel.Worksheet obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Worksheets add(Excel.Worksheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

    public Excel.Sheets add(Excel.Sheets obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }


    public Excel.Workbooks add(Excel.Workbooks obj)
    {
        _interopObjectList.Add(obj);
        return obj;
    }

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

    //Release all registered interop objects in reverse order
    public void unregister()
    {
        //Loop object list in reverse order and release Office object
        for (int i=_interopObjectList.Count-1; i>=0 ; i -= 1)
        { ReleaseComObject(_interopObjectList[i]); }

        //Clear object list
        _interopObjectList.Clear();
    }


    /// <summary>
    /// Release a com interop object 
    /// </summary>
    /// <param name="obj"></param>
     public static void ReleaseComObject(object obj)
     {
         if (obj != null && InteropServices.Marshal.IsComObject(obj))
             try
             {
                 InteropServices.Marshal.FinalReleaseComObject(obj);
             }
             catch { }
             finally
             {
                 obj = null;
             }

         GC.Collect();
         GC.WaitForPendingFinalizers();
         GC.Collect();
         GC.WaitForPendingFinalizers();
     }

Тогда принцип состоит в том, чтобы создать класс и зафиксировать ссылки следующим образом:

//Create helper class
xlsHandler xlObj = new xlsHandler();

..

//Sample - Capture reference to excel application
Excel.Application _excelApp = xlObj.add(new Excel.Application());

..
//Sample - Call .Autofit() on a cell range and capture reference to .Columns() 
xlObj.add(_targetCell.Columns).AutoFit();

..

//Release all objects collected by helper class
xlObj.unregister();

Not возможно, код большой красоты, но может вдохновить на что-то полезное.

54
задан jpbochi 17 November 2009 в 08:39
поделиться

16 ответов

Обработчики событий являются очень общим источником неочевидных утечек памяти. Если Вы подписываетесь на событие на object1 от object2, то сделайте object2. Расположите () и притворитесь, что это не существует (и опускает все ссылки от Вашего кода), существует неявная ссылка в событии object1, которое будет препятствовать тому, чтобы object2 был собран "мусор".

MyType object2 = new MyType();

// ...
object1.SomeEvent += object2.myEventHandler;
// ...

// Should call this
// object1.SomeEvent -= object2.myEventHandler;

object2.Dispose();

Это - общий падеж утечки - упущение легко отказаться от подписки от событий. Конечно, если object1 будет собран, то object2 будет собран также, но только в тогда.

63
ответ дан Reed Copsey 7 November 2019 в 07:35
поделиться

В то время как возможно, что что-то в платформе имеет утечку, больше тогда, вероятно, у Вас есть что-то, что не является избав правильно, или что-то блокирует GC от избавления от него, IIS был бы главным кандидатом на это.

Просто помнят, что не все в.NET - полностью управляемый код, взаимодействующий с COM, файл io как потоки файла, запросы DB, изображения, и т.д.

проблема А мы имели только что (.net 2.0 на IIS 6) был то, что мы создадим изображение и затем избавимся от него, но IIS не освободил бы память некоторое время.

0
ответ дан Bob The Janitor 7 November 2019 в 07:35
поделиться

Единственные утечки (кроме ошибок во времени выполнения, которое может присутствовать, хотя не, ужасно вероятно, из-за сборки "мусора") будут для собственных ресурсов. Если Вы P/Invoke в собственную библиотеку, которая открывает дескрипторы файлов или сокетные соединения, или безотносительно от имени Вашего управляемого приложения и Вас никогда явно, закрывают их (и не обрабатывайте их в устройстве обработки отходов или деструкторе/финализаторе), у Вас могут быть утечки памяти или утечки ресурсов, потому что время выполнения не может управлять всеми теми, которые автоматически для Вас.

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

0
ответ дан Michael Trausch 7 November 2019 в 07:35
поделиться

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

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

1
ответ дан Ben S 7 November 2019 в 07:35
поделиться

Это - миф, что Вы не можете пропустить память в управляемом коде. Предоставленный, это намного более твердо, чем в неуправляемом C++, но существует миллион способов сделать это. Статические объекты, содержащие ссылки, ненужные ссылки, кэширование, и т.д. при выполнении вещей "правильный" путь, многие объекты, не будут значительно позже собираться "мусор", чем необходимый, который является видом утечки памяти, слишком по-моему, в практическом и не теоретическом пути.

, К счастью, существуют инструменты, которые могут помочь Вам. Я использую Microsoft Профилировщик CLR много - это не самый удобный для пользователя инструмент, никогда записанный, но это определенно очень полезно, и это свободно.

4
ответ дан Tamas Czinege 7 November 2019 в 07:35
поделиться

Отражение испускает , другой потенциальный источник утечек, с, например, встроенного объекта deserializers и необычного SOAP/клиентов XML. По крайней мере, в более ранних версиях платформы, сгенерированный код в зависимом AppDomains никогда не разгружался...

5
ответ дан Pontus Gagge 7 November 2019 в 07:35
поделиться

Единственная причина утечки памяти в приложении.NET состоит в том, что на объекты все еще ссылаются, хотя их продолжительность жизни закончилась. Следовательно, сборщик "мусора" не может собрать их. И они становятся долговечными объектами.

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

7
ответ дан tranmq 7 November 2019 в 07:35
поделиться

Как уже упомянуто ссылки хранения вокруг приведут к увеличению использования памяти со временем. Простой способ войти в эту ситуацию с событиями. Если у Вас был длинный живущий объект с некоторым событием, которое слушают Ваши другие объекты, если слушатели никогда не будут удаляться тогда, то событие на долговечном объекте поддержит те другие экземпляры еще долго после того, как они больше не будут необходимы.

7
ответ дан toad 7 November 2019 в 07:35
поделиться

При разработке приложения WinForms тонкая "утечка" Control.AllowDrop свойство (раньше включал Перетаскивание). Если AllowDrop будет установлен на "истинный", то CLR будет все еще содержать на Ваше Управление хотя System.Windows.Forms.DropTarget. Для фиксации этого удостоверьтесь Ваш Control AllowDrop, свойство установлено на false, когда Вам больше не будет нужно оно, и CLR будет заботиться об остальных.

17
ответ дан Zach Johnson 7 November 2019 в 07:35
поделиться

Делегаты могут привести к неинтуитивным утечкам памяти.

Каждый раз, когда Вы создаете делегата из метода экземпляра, ссылка на тот экземпляр хранится "в" том делегате.

Кроме того, при объединении нескольких делегатов в многоадресного делегата у Вас есть один большой блоб ссылок на многочисленные объекты, которым мешают быть собранными "мусор", пока тот многоадресный делегат используется где-нибудь.

20
ответ дан Christian Klauser 7 November 2019 в 07:35
поделиться

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

то, Что Вы видите в.NET, не является утечками памяти, но возражает, что никогда не располагаются. Объект не будет расположен, пока сборщик "мусора" может найти его на графе объектов. Таким образом, если какой-либо живущий объект где-нибудь будет иметь ссылку на объект, то он не будет расположен.

регистрация События является хорошим способом заставить это произойти. Если объект регистрируется для события, независимо от того, что он зарегистрировался в, имеет ссылку на него, и даже если Вы устраняете любую ссылку на объект, пока он не не регистрируется (или объект, в котором он зарегистрировался, становится недостижимым), он останется в живых.

, Таким образом, необходимо не упустить объекты, которые регистрируются для статических событий без ведома. Изящная функция эти ToolStrip, например, то, что при изменении темы дисплея она автоматически восстановит изображение в новой теме. Это выполняет эту изящную функцию путем регистрации для помех SystemEvents.UserPreferenceChanged событие. При изменении темы Windows событие генерируется, и весь из ToolStrip, объекты, которые слушают событие, уведомляются, что существует новая тема.

Хорошо, поэтому предположите, что Вы решаете выбросить ToolStrip на Вашей форме:

private void DiscardMyToolstrip()
{
    Controls.Remove("MyToolStrip");
}

Вы теперь имеете ToolStrip, который никогда не будет умирать. Даже при том, что это больше не находится на Вашей форме, каждый раз, когда пользовательский Windows тем изменений покорно скажет иначе несуществующее ToolStrip об этом. Каждый раз выполнения сборщика "мусора", это думает, что "Я не могу выбросить тот объект, UserPreferenceChanged, событие использует его".

Это не утечка памяти. Но это могло бы также быть.

Вещи как это делают профилировщика памяти неоценимым. Выполните профилировщика памяти, и Вы скажете, что "это нечетно, кажется, существует десять тысяч ToolStrip объекты на "куче", даже при том, что существует только один на моей форме. Как это происходило?"

, О, и в случае, если Вы задаетесь вопросом, почему некоторые люди думают, методы set свойства являются злыми: чтобы заставить ToolStrip не регистрироваться от UserPreferenceChanged событие, установите Visible свойство к [1 112].

27
ответ дан Robert Rossney 7 November 2019 в 07:35
поделиться

Я не думаю, что утечки памяти стиля C++ возможны. Сборщик "мусора" должен составлять их. Возможно создать статический объект, который агрегировал ссылки на объект даже при том, что объекты никогда не используются снова. Что-то вроде этого:

public static class SomethingFactory
{
    private static List<Something> listOfSomethings = new List<Something>();

    public static Something CreateSomething()
    {
        var something = new Something();
        listOfSomethings.Add(something);
        return something;
    }
}

Это - очевидно глупый пример, но это был бы эквивалент управляемой утечки памяти во время выполнения.

40
ответ дан strager 7 November 2019 в 07:35
поделиться

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

, Например, если Вы инстанцируете объектов в список и затем забываете удалять их из списка, когда Вы сделаны И забываете располагать их.

2
ответ дан Kevin Laity 7 November 2019 в 07:35
поделиться

На моей последней работе мы использовали стороннюю библиотеку .NET SQLite, которая просачивалась как решето.

Мы выполняли много быстрых вставок данных в странной ситуации, когда база данных соединение приходилось каждый раз открывать и закрывать. Сторонняя библиотека сделала то же открытие соединения, которое мы должны были сделать вручную, и не задокументировала это. В нем также были ссылки, которых мы никогда не нашли. В результате было открыто в 2 раза больше соединений, чем предполагалось, и только половина закрывалась. И поскольку ссылки были сохранены, у нас была утечка памяти.

Это явно не то же самое, что классическая утечка памяти C / C ++, но для всех намерений и целей она была для нас одной.

0
ответ дан 7 November 2019 в 07:35
поделиться
6
ответ дан 7 November 2019 в 07:35
поделиться

Не на самом деле утечка памяти, но довольно легко заканчиваться памятью при использовании больших объектов (превышает 64 кк, если я запомню правильно). Они хранятся на Лохе, и это не дефрагментировано. Поэтому, используя эти большие объекты и освобождая их, освобождают память на LOH, но эта свободная память больше не используется .NET Runtime для этого процесса. Таким образом, вы можете легко заканчиваться пространством на LOH, используя лишь несколько больших объектов на LOH. Эта проблема известна Microsoft, но, как я помню, теперь это решение для этого планируется.

1
ответ дан 7 November 2019 в 07:35
поделиться
Другие вопросы по тегам:

Похожие вопросы: