MEF хранит ссылку на части NonShared IDisposable, не позволяя сборщику мусора собирать их.

Я столкнулся с некоторой проблемой во время жизни части MEF, которая вызывает утечку памяти в моем приложении Prism.

Мое приложение экспортирует представления и модели представления, при этом для параметра PartCreationPolicy установлено значение CreationPolicy.NonShared. Представления и модели представления наследуются от ViewBase и ViewModelBase соответственно, что реализует IDisposable.

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

Контейнер не будет содержать ссылки на создаваемые им части, если не выполняется одно из следующих условий:

  • Деталь отмечена как Shared
  • Часть реализует IDisposable
  • Один или несколько импортов настроены на рекомпозицию

Как же тогда сделать так, чтобы MEF не хранил ссылки на эти части? Есть ли атрибут, который я могу использовать, чтобы сообщить MEF, что я не хочу, чтобы он сохранял ссылку на мою часть, даже если он реализует IDisposable?

Обе стратегии, рассмотренные в статье выше, не кажутся мне хорошими решениями:

  • ReleaseExport требует объект Export в качестве параметра, который я не знаю, как предоставить. У меня есть экземпляр моего представления, но я не могу узнать, какой контракт использовался для создания представления. Было бы здорово, если бы была перегрузка для ReleaseExport, которая могла бы получать любой объект, созданный контейнером.
  • Использование дочернего контейнера также не кажется естественным вариантом.

Любая помощь будет оценена.


person Adi Lester    schedule 09.01.2012    source источник


Ответы (4)


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

Существует три подхода MEF к решению этой проблемы, все упомянутые другими респондентами:

  • ExportFactory<T>
  • Дочерние контейнеры
  • ReleaseExport()

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

В MEF нет ReleaseExportedObject(), потому что несколько экспортов (например, свойств) могут возвращать одно и то же значение; это может быть логически возможно обеспечить, но добавленная сложность делает маловероятным решение MEF в обозримом будущем.

Надеюсь это поможет; Я пометил этот вопрос как «призма», так как уверен, что другие члены сообщества Prism сталкивались с этим и могли дать совет.

person Nicholas Blumhardt    schedule 09.01.2012
comment
Спасибо за ответ и за приветственный ретег. Я предполагаю, что интеграция с Prism вместе с ExportFactory — это правильный путь, хотя это кажется излишним для простого запроса, так как не добавляйте меня в контейнер. Я еще не сдался - я все еще ищу более простое и элегантное решение для этого. - person Adi Lester; 10.01.2012

Когда вы реализуете IDisposable, вы как бы говорите, что тип должен быть очищен детерминированным способом (путем вызова IDisposable.Dispose, а не случайным образом, когда сборщик мусора решит, что пришло время.

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

  • Не реализуйте IDisposable в своих моделях представления. Очевидно, вас все равно не волнует, когда они будут убраны, так зачем делать их IDisposable?

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

По сути, если что-то одноразовое, это также должно быть разумной точкой в ​​вашем коде, где вам нужно избавиться от того, что одноразовое.

person Martin Liversage    schedule 09.01.2012
comment
Я думаю, вы согласитесь, что довольно глупо, что я реализую IDisposable для того, чтобы избавиться от объекта раньше, чем позже, и добиться прямо противоположного результата благодаря реализации контейнера MEF. Отказ от реализации IDisposable на моих виртуальных машинах не является реальным вариантом, поскольку мне нужно выполнить очистку, например, отменить регистрацию составных команд — если я этого не сделаю, виртуальные машины не будут подвергнуты сборке мусора. Мои представления отвечают за удаление своих виртуальных машин, но в любом случае вызов Dispose() не удаляет ссылку на объект из контейнера, так что это все равно не помогает. - person Adi Lester; 09.01.2012
comment
Я думаю, что использование моего второго предложения по внедрению фабрики модели представления в каждое представление вместо модели представления, созданной MEF, должно решить вашу проблему. Очевидно, что создание фабрики для каждой модели представления становится довольно утомительным, но, тем не менее, именно этим я часто занимаюсь, используя текущую версию MEF для внедрения зависимостей. - person Martin Liversage; 09.01.2012

Эти экземпляры следует создавать с помощью импортированной ExportFactory‹. Т›. После этого у вас будет необходимый контроль, чтобы избавиться от них через ExportLifetimeContext<T>.Dispose().

Однако это доступно только в следующей версии .NET (4.5) или в последних предварительных выпусках MEF на codeplex. (В более ранних версиях MEF та же функциональность была реализована в качестве примера и называлась PartCreator, как описано в этом сообщение в блоге.)

person Wim Coenen    schedule 09.01.2012
comment
Я не упомянул об этом в своем вопросе, но я использую Prism для создания своих представлений, поэтому я не могу получить доступ к этой части кода для получения объекта ExportFactory. Даже если бы я мог, существует проблема доступа к объекту фабрики из того места в коде, где я хочу избавиться от представления, и использования ExportFactory для каждого объекта NonShared, что очень неинтуитивно и не кажется хорошей практикой. - person Adi Lester; 09.01.2012
comment
@Lester: вы должны использовать ExportFactory только для объектов, которые должны быть явно созданы/удалены импортером (что позволяет создавать объекты с более коротким сроком службы, чем у контейнера MEF). Я не говорю, что вы должны использовать ExportFactory везде, где у вас есть экспорт NonShared. - person Wim Coenen; 09.01.2012

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

person Adi Lester    schedule 06.01.2014