Чрезмерное использование памяти в C # с большим количеством COM-объектов

У меня есть приложение, изначально написанное на VB6, которое я использовал инструмент для преобразования в C # с довольно неплохим успехом с функциональной точки зрения. Он обрабатывает большой объем сообщений с использованием большого количества объектов COM (C ++) малого и среднего размера.

Я заметил, что конкретный тестовый запуск в старом приложении VB6, который работал с использованием менее 40 МБ памяти, требовал почти 900 МБ в приложении C #. Если я помещаю GC.Collect () в самый внутренний цикл обработки сообщений приложения C #, он использует столько же или меньше памяти, что и приложение VB6, хотя тогда он действительно очень медленный. Это заставляет меня думать, что «утечки» в абсолютном смысле слова нет.

Затем я запустил приложение C # через профилировщик памяти AQTime, и он сообщил, что в куче находится чрезмерное количество объектов COM / C ++. Я предположил, что это произошло из-за того, что вызываемые оболочки времени выполнения вокруг COM-объектов были довольно маленькими и никогда (или редко) запускали сборку в C #, даже если их COM-объекты, на которые они ссылаются, были значительно больше. Я думал, что могу решить эту проблему, добавив явные вызовы Marshal.ReleaseComObject () вокруг COM-объектов в приложении C #. Я пошел и сделал это во многих местах, где время жизни COM-объектов было легко определить. Я заметил лишь очень небольшое уменьшение использования памяти.

Мне интересно, почему я не добился большего успеха в этом. Просматривая статические методы в классе Marshal, я вижу некоторые, которые наводят меня на мысль, что либо мне не хватает тонкости в обработке ссылок COM, либо мое предположение о том, что они немедленно уничтожаются, когда счетчик ссылок RCW достигает нуля, неверно. .

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


person Dan Hermann    schedule 15.03.2011    source источник
comment
Что произойдет, если вы не сделаете GC.Collect? Это вылетает?   -  person John Saunders    schedule 15.03.2011
comment
Если я удалю вызов GC.Collect (), использование памяти составит около 900 МБ. Он никогда не падал, но такое использование памяти неприемлемо, когда старое приложение VB6 использовало менее 40 МБ при использовании тех же COM-объектов.   -  person Dan Hermann    schedule 15.03.2011
comment
@Dan: это виртуальная память, а не физическая. Чрезмерное разбиение на страницы было бы лучшей мерой, чем общий объем используемой памяти.   -  person John Saunders    schedule 15.03.2011
comment
Хм, я основывал использование памяти на том, что было сообщено диспетчером задач, что может быть не самым точным, но, согласно AQTime, размер управляемой кучи был значительно меньше 100 МБ, поэтому размер COM-объектов был примерно в 10 раз больше. Разве они не должны быть немедленно освобождены, как только их счетчик ссылок RCW достигнет нуля?   -  person Dan Hermann    schedule 15.03.2011
comment
Верно, диспетчер задач безнадежно неточен для такого рода вещей. Используйте другой профилировщик памяти.   -  person Cody Gray    schedule 15.03.2011
comment
@Dan: Я бы вообще не стал беспокоиться о профилировании, пока не возникнет реальная проблема, которую нужно профилировать. Вы легко можете обнаружить, что отлично справляетесь с неправильной проблемой. Опыт говорит.   -  person John Saunders    schedule 15.03.2011


Ответы (1)


Извините за ссылку вместо хорошего синопсиса, но у меня никогда не было этой проблемы, поскольку я имел дело с IE и mshtml в долгоживущем сценарии.

В статье говорится:

При использовании COM-объекта из приложения на основе .NET задействованы два объекта: RCW и COM-объект (или объекты). При сборке мусора учитывается только размер RCW (который может быть небольшим), но не размер COM-объекта (который может быть большим). Следовательно, хотя приложение на основе .NET может освободить RCW, сборка мусора может не вернуть RCW, даже если память исчерпана. Пока RCW остается в памяти, COM-объект, которым он управляет, также остается в памяти.

Есть два механизма, которые обеспечивают освобождение COM-объектов из памяти: объект AppDomain и метод ReleaseComObject. Использование AppDomain обеспечивает простейшее решение для управления COM-объектами, но требует снижения производительности и может подвергнуть риску безопасность. Использование ReleaseComObject позволяет избежать этих затрат, но требует более тщательного планирования и кодирования.

person Ritch Melton    schedule 15.03.2011
comment
Спасибо, я не знал о параметре AppDomain, но он может быть не идеальным для моей ситуации, поскольку во внутреннем цикле обработки сообщений важна производительность. - person Dan Hermann; 15.03.2011
comment
@Dan - Я думал, что ваш цикл создает и выпускает довольно много COM-объектов. Если так, то это само по себе не очень эффективно. Я предполагаю, что ReleaseComObject - действительно хороший вариант, который стоит реализовать и профилировать. - person Ritch Melton; 15.03.2011