Dll, отображение памяти, базовый адрес, использование памяти, and.NET?

прежде чем я начну с реального вопроса, позвольте мне просто сказать, что я могу получить некоторые детали здесь неправильно. Если да, пожалуйста, Арестуйте меня на них, а также или даже Вместо ответа на мой вопрос.

мой вопрос касается DLL и .NET, в основном. У нас есть приложение, которое использует довольно много памяти, и мы пытаемся выяснить, как правильно измерить это, особенно когда проблема в основном возникает на компьютерах клиентов.

одна вещь, которая поразила меня что у нас есть довольно большие .NET-сборки с сгенерированным ORM-кодом.

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

вопрос в том, что происходит с сборкой .NET. Эта DLL содержит IL, и хотя эта часть его может быть разделен между приложениями, как насчет JITted-кода, который является результатом этого IL? Это общее? Если нет, то как я могу измерить, чтобы выяснить, действительно ли это способствует проблеме или нет? (Да, я знаю, это будет способствовать, но я не собираюсь тратить много времени на это, пока это не станет самой большой проблемой).

кроме того, я знаю, что мы не смотрели на базовый адрес для всех .NET-сборок в нашем решении, это необходимо для .NET-сборок? И если да, то существуют ли какие-либо рекомендации по определению этих адресов?

любое понимание этой области было бы очень желательно, даже если окажется, что это не большая проблема, или даже не проблема вообще.


редактировать: только что нашел этот вопрос:.NET сборки и DLL перебазирование что частично отвечает на мой вопрос, но я все равно хотел бы знать, как JITted факторы кода во все это.

из этого следует, что вопрос и его принятый ответ, что JITted-код помещается в кучу, что означает, что каждый процесс загрузит общий двоичный образ сборки и создаст частную JITted-копию кода в своем собственном пространстве памяти.

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


редактировать: добавлен более короткий список вопросов здесь:

  1. есть ли смысл в том, чтобы убедиться, что базовые адреса .NET-сборок уникальны и не перекрываются, чтобы избежать перезагрузки dll, которая в основном будет использоваться только для получения кода IL для JITting?
  2. как я могу измерить, сколько памяти используется для JITted-кода, чтобы выяснить, действительно ли это проблема или нет?

ответ @Brian Rasmussen здесь показывает что Джиттинг произведет в-процесс копии JITted-кода, как я и ожидал, но эта перезагрузка сборок фактически повлияет на сокращение использования памяти. Мне придется копаться в инструментах WinDbg+SoS, которые он упоминает, что-то у меня было в моем списке некоторое время, но теперь я подозреваю, что больше не могу откладывать :)


редактировать: некоторые ссылки, которые я нашел на эту тему:

3 ответов


Это для вопроса 1)

jitted код помещается в специальную кучу. Вы можете проверить эту кучу, используя !eeheap команда в WinDbg + SoS. Таким образом, каждый процесс будет иметь свою собственную копию jitted-кода. Команда также покажет вам общий размер кучи кода.

Дайте мне знать, если вы хотите получить дополнительную информацию о получении этой информации от WinDbg.

Это для вопроса 2)

по данным книга эксперт .NET 2.0 IL Ассамблеи the .reloc часть PE-файла pure-IL содержит только одну запись fixup для заглушки запуска среды CLR. Таким образом, количество исправлений, необходимых для управляемой DLL во время перезагрузки, довольно ограничено.

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


Я не уверен, насколько точна следующая информация с более новыми версиями .NET и / или версиями Windows. MS может решить некоторые проблемы загрузки/совместного использования DLL с первых дней .Сеть. Но я считаю, что многое из нижеследующего все еще применимо.

с .NET-сборками большое преимущество совместного использования страниц между процессами (и между сеансами сервера терминалов) исчезает, потому что JIT должен писать собственный код на лету - нет файла изображения для резервное копирование собственного кода. Таким образом, каждый процесс получает свои собственные, отдельные страницы памяти для jitted-кода.

Это похоже на проблемы, которые вызваны неправильным использованием DLL - библиотек-если ОС необходимо выполнить исправления на стандартной DLL Win32 при загрузке, страницы памяти для исправленных частей не могут быть общими.

однако, даже если jitted-код не может быть общим, есть преимущество в переназначении DLL .NET, потому что DLL все еще загружается для метаданных (и IL) - и этот материал можно разделить, если никакие исправления не требуются.

с помощью ngen можно обмениваться страницами памяти с .NET-сборкой. но это влечет за собой целый ряд проблем.

см. Этот старый пост в блоге Джейсона Зандера для некоторых деталей:

http://blogs.msdn.com/jasonz/archive/2003/09/24/53574.aspx

Ларри Остерман есть статья приличный блог по обмену страницы DLL и эффект исправления:

http://blogs.msdn.com/larryosterman/archive/2004/07/06/174516.aspx


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

как .NET, так и стандартный Win32 DLL разделяют код между различными процессами, используя их. В случае .NET это верно только для DLL с той же сигнатурой версии, так что две разные версии одной и той же dll могут быть загружены в память одновременно.

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

Так что да, на самом деле DLL код загружается один раз и делится между абонентами, но инструкции кода (и, следовательно, распределения) происходят отдельно в вызов пространства процесса.

Edit: Хорошо, давайте посмотрим, как JIT работает с сборками .NET.

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