#c# #.net
#c# #.net
Вопрос:
Последние пару дней я бился головой о стену, пытаясь разобраться в проблеме с доменом приложения. Я создал небольшую службу Windows, которая запускает плагины. Это работает так: каждый плагин представляет собой библиотеку dll, которая загружается в свой отдельный appdomain с помощью «CreateInstanceAndUnwrap», и я сохраняю ссылку на appdomain, и как только плагин завершается, я закрываю appdomain, созданный для этого плагина. я использую теневое копирование, чтобы разрешить обновления дляплагины, пока они работают в фреймворке.Хорошо, пока все хорошо. Он работал в течение 6 месяцев, пока на прошлой неделе я не решил поместить основные функциональные возможности фреймворка в отдельную DLL и выполнить эту dll в отдельном appdomain .Итак, как это работает прямо сейчас :
Появляется служба Windows. основная dll загружается и выполняется с использованием CreateInstanceAndUnwrap, который, в свою очередь, отвечает за запуск плагинов в их собственных отдельных доменах приложений (с использованием CreateInstanceand ..)
У меня есть несколько разных расположений для сборок: папка Bin (в папке Bin службы Windows хранятся только библиотеки dll, используемые службой) Папка основных библиотек DLL (основные библиотеки dll попадают сюда) Папка ссылок (любые ссылки попадают сюда) Папка плагинов (плагины попадают сюда)
Я разрешаю любую найденную dll, подключаясь к обработчику onassemblyresolve каждого appdomain. Таким образом, это означает, что библиотеки DLL могут загружаться по сети.
Теперь проблема в том, что служба Windows работала в течение одного дня, а объем памяти достигал 1,5G. Я создал дамп памяти, и кажется, что загруженные модули составляют всего 100 МБ от всего 1.5G, поэтому я не знаю, куда уходит вся эта память. Используя debugdiag, я увидел предупреждение о фрагментации кучи, но я понятия не имею, с чего мне начать диагностировать проблему. Обычно, когда фреймворк работает в течение дня, он потребляет что-то вроде 100 млн. Также это не связано с плагинами, поскольку, когда я откатил свои изменения в фреймворке, использование memroy стало нормальным, как и раньше. Когда я создаю appdomains для плагинов, я переключаю базовый путь и путь к корзине appdomain на папки CORE и References, в которых находится большинство библиотек DLL, в надежде, что разрешение сборки вызывается реже.
Я просмотрел журналы слияния, а также прочитал немного больше о контекстах загрузки, таких как default, from и none, но я не уверен, что это правильный путь.
Есть идеи?
Комментарии:
1. я использую интерфейс для получения прокси-сервера второго appdomain, и недавно возникла ошибка, связанная с ошибкой приведения из-за знаменитого «недопустимого приведения из исключения типа», поэтому вместо этого я переключил возвращаемое приведение на динамический тип: D я знаю, что это не очень хорошее решение проблемы, ноэто решило проблему с приведением. я предполагаю, что в вызывающий appdomain загружается несколько версий библиотеки dll интерфейса, и именно поэтому он выдает исключение приведения типа. я создаю основной домен приложения, а также подключаемые домены приложений, используя createinstanceandunwrap.для разрешения события asselmbly я использую «LoadFrom»
Ответ №1:
Я думаю, что это:
когда я решил поместить основные функциональные возможности фреймворка также в отдельную DLL и выполнить эту dll в отдельном appdomain.Итак, как это работает прямо сейчас :
Появляется служба Windows. основная dll загружается и выполняется с использованием CreateInstanceAndUnwrap, который, в свою очередь, отвечает за запуск плагинов в их собственных отдельных доменах приложений (с использованием CreateInstanceand ..)
может быть, это противоречит этому:
Из http://msdn.microsoft.com/en-us/library/3c4f1xde.aspx
Если вы выполняете ранний вызов метода M объекта типа T1, который был возвращен CreateInstanceAndUnwrap, и этот метод выполняет ранний вызов метода объекта типа T2 в сборке C, отличной от текущей сборки или сборки, содержащей T1, сборка C являетсязагружается в текущий домен приложения. Эта загрузка происходит, даже если ранний связанный вызов T1.M() был выполнен в теле DynamicMethod или в другом динамически генерируемом коде. Если текущий домен является доменом по умолчанию, сборка C не может быть выгружена до завершения процесса.
В этом случае вы можете использовать отражение для выполнения «соответствующего» вызова, чтобы устранить раннюю привязку.
Комментарии:
1. Я продолжу расследование, если это то, что происходит, но когда я отлаживал службу Windows, я не видел, чтобы какие-либо дублирующиеся библиотеки DLL загружались или основной appdomain загружался с библиотеками DLL из плагинов вообще. казалось, все в порядке, даже когда я сбросил запущенную службу 1.5G, debugdiag не показывал никаких признаков этого. Загруженные модули = 100M!
2. Также в документе говорится, что: «Эта загрузка происходит, даже если ранний связанный вызов T1.M() был выполнен в теле DynamicMethod или в другом динамически генерируемом cod». Таким образом, это будет означать, что использование отражения, вероятно, также не будет работать. (Если это так)
3. Отражение все еще можно использовать для вызова кода, который не генерируется динамически. Например, использование Reflection MethodInfo не генерирует никакого динамического кода и может использоваться для вызова методов, определенных во время компиляции. Поэтому я думаю, что все еще есть возможность прервать раннее связывание.
4. Я пробовал это, но GetMethod, похоже, не работает с динамически генерируемым прокси. Я получаю исключение «метод не существует в system.MarshalByRef».
5. также кажется, что это МОЖЕТ быть причиной проблем с памятью в моем случае, я обнаружил, что некоторые из плагинов фактически загружаются в начальный appdomain. (но они не должны принимать 1.5G, поэтому я все еще не уверен)