Приложение C # Winforms - Большие объекты застряли в Gen 2 - Проблемы с памятью - PullRequest
1 голос
/ 20 августа 2011

Я начал профилировать память нашего приложения, потому что недавно мы получили несколько отчетов о производительности и исключениях нехватки памяти. Приложение разработано на C # .Net Winforms (.NET Framework 2.0)

Когда приложение запускалось, профилировщик ANT показывает 17,7 МБ объектов, работающих в Gen 2.

Когда приложение запускается, оно читает почтовые индексы 77000+ из сериализованного файла xml на диске и сохраняет их в Hashtable. Пожалуйста, смотрите пример кода ниже

public Class ZipCodeItem
{
    private string zipCode;
    private string city;
    private string state;
    private string county;
    private int tdhCode;
    private string fipsCounty;
    private string fipsCity;

    Public ZipCodeItem()
    {
         // Constructor.. nothing interesting here
    }

    // Bunch of public getter/setter properties
}

Вот статический класс, который считывает сериализованные данные почтового индекса из файла на диске и загружает почтовые индексы.

internal sealed class ZipTypes
{
    private static readonly Hashtable zipCodes = new Hashtable();

    public static ArrayList LookupZipCodes(string zipCode)
    {
        if (zipCodes.Count == 0)
            LoadZipCodes();

        ArrayList arZips = new ArrayList();

        // Search for given zip code and return the matched ZipCodeitem collection
        if (zipCodes.ContainsKey(zipCode))
        {
             // Populate the array with the matched items
        }

        // Omitted the details to keep it simple

        return arZips;
    }

    private static bool LoadZipCodes()
    {
        using (FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            // unzip it.. Omitted the details to keep it simple
            // Read the zipcodes from the flat xml file on disk and load the local zipCodes HashTable
        }
    }
}

Этот класс и корр. ZipCodes доступны по всему приложению.

Приблизительно 14 мегабайт из 17,7 мегабайт объектов Gen 2 являются либо классами zipCodeItems, либо его дочерними классами String.

Я хотел бы изменить свой код на такой, чтобы НЕ сохранять эти объекты с 77000+ объектами почтового индекса в памяти (в хеш-таблице), но предоставлять сопоставленные элементы zipCode, когда приложению это необходимо.

Любые предложения Как решить эту проблему? Заранее спасибо.

Ответы [ 4 ]

3 голосов
/ 20 августа 2011

Я собираюсь избегать прямого ответа на вопрос в надежде дать более полезный ответ, потому что я не верю, что ~ 14 МБ, связанные с этим хешем, действительно вызывают проблему.

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

Мы можем с уверенностью предположить, что ваш хэш не вызывает OutOfMemoryException, так как он еще недостаточно велик для этого. Оставьте это как есть, за исключением двух небольших изменений:

  1. Используйте строго типизированный Dictionary<K,V> вместо Hashtable. Hashtable по существу устарел, как только в .NET 2.0 появилось обобщение Вы также можете пойти дальше и заменить это ArrayList на List<T>.
  2. Вместо выполнения Contains и последующего поиска значения в хэше используйте TryGetValue. Это сокращает количество просмотров хеш-таблицы в два раза. Теперь это может не являться узким местом в производительности вашего приложения, но я не думаю, что это равнозначно преждевременной оптимизации.

Теперь рассмотрим суть проблемы ...

У вас есть результаты профилировщика. Вернитесь назад и посмотрите, где находится ваша память. Для того, чтобы проверить эти вещи:

  1. Содержит ли .NET большую часть памяти или это собственный код (возможно, создается множество объектов, которые реализуют IDisposable, и не вызывает Dispose() своевременно для них.) Если это последнее, вы, вероятно, знаете, где смотреть.
  2. Как выглядит куча больших объектов (LOH)? Большая часть памяти выделена там? Многие большие выделения могут фрагментировать LOH, и он может не уплотняться в течение достаточно долгого времени. ANTS сообщит вам об этом в правом верхнем углу страницы обзора результатов.
  3. Обработчики событий. Когда объект подписывается на событие, подписчик сохраняет ссылку на подписчика (метод) (MultiCastDelegate, то есть объект события). Это может привести к тому, что время жизни объекта никогда не закончится, и через некоторое время это может привести к увеличению объема памяти. Вы должны убедиться, что, если есть объекты, которые создаются и затем выходят из области видимости, он также отменяет подписку на любые события, на которые он ранее подписался. Статические события могут быть убийцей здесь.
  4. Используйте ANTS для отслеживания времени жизни объекта. Аналогично приведенному выше, убедитесь, что нет объектов, которые случайно остались живыми из-за устаревших ссылок. Это может произойти легче, чем вы думаете. Опять же, обратите внимание на области, где создано относительно большое количество объектов, и они выходят за пределы области видимости, а также на случаи, когда другие объекты поддерживают ссылки на них. ANTS может показать это на графике объектов.

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

0 голосов
/ 20 августа 2011

При запуске приложения профилировщик ANT отображает 17,7 МБ объектов жить в Gen 2

У нас была похожая проблема, и мы находим, что можно хранить эти данные в памятке , а не загружать ее несколько раз . НО он не должен загружаться при запуске. Измените логику, чтобы загрузить ее при первом использовании по требованию. Потому что могут быть случаи, когда она вообще не используется. Нет смысла хранить этот кусок в Gen2 для таких случаев.

Я уверен, что могут быть серьезные проблемы с утечкой памяти, чем эта, если вы есть проблемы с памятью в вашем приложении. ANT профилировщик хорош для такие случаи: -)

0 голосов
/ 20 августа 2011

Если ваши пользователи жалуются на исключения, связанные с нехваткой памяти, и вы обращаетесь к тому, что занимает 15 МБ памяти, вы ищете не в том месте.

В остальной части моего ответа предполагается, что 15 МБ действительно имеют значение.

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

Мы загружаем 3 ГБ данных IP в пространство процессов наших веб-серверов. Тем не менее, к большинству IP-адресов не обращаются большую часть времени (особенно учитывая, что мы имеем сильный географический уклон в пользовательской базе, но по-прежнему необходимо иметь данные, доступные для посетителей из менее часто посещаемых частей света).

Чтобы сохранить небольшой объем памяти и очень быстрый доступ, мы использовали файлы, отображенные в памяти. Хотя поддерживает их встроенные в .NET 4 , вы все равно можете использовать их через вызовы взаимодействия в любой предыдущей версии .NET.

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

0 голосов
/ 20 августа 2011

Вам понадобится какое-то хранилище вместе с механизмом легкого доступа.

Я бы предложил использовать какую-либо форму базы данных на основе SQL.

Мы добились хорошего успеха при использовании SQL Compact Edition, поскольку его можно развертывать без предварительных условий (так называемое «частное развертывание»). Впоследствии история интеграции для запросов очень трудна - например, использовать Linq to SQL очень просто.

Вы также можете посмотреть на SQL Lite или других провайдеров. Я бы отвел вас от зависимости SQL Express, хотя бы потому, что она будет слишком тяжелой для этих нужд.

Кроме того, учитывая, что теперь вы будете кэшировать почтовые индексы в базе данных, вы можете также рассмотреть вопрос о некотором «процессе синхронизации» (скажем, ежедневно) для загрузки и анализа входного XML-файла, который вы упомянули выше ( при условии, что файл XML получен из некоторого веб-сервиса), иначе вы можете просто развернуть базу данных, уже заполненную данными.

...