Принуждение Windows к загрузке DLL в местах, так что память минимально фрагментирована - PullRequest
8 голосов
/ 17 ноября 2011

Мое приложение нуждается в большом количестве памяти и большой структуре данных для выполнения своей работы. Зачастую приложению требуется более 1 ГБ памяти, а в некоторых случаях моим клиентам действительно необходимо использовать 64-разрядную версию приложения, поскольку они имеют несколько гигабайт памяти.

В прошлом я мог легко объяснить пользователю, что, если объем используемой памяти достигает 1,6–1,7 ГБ, это означает «нехватка памяти» или она действительно близка к ситуации «нехватка памяти», и что ему необходимо уменьшить их память или перейти на 64-битную версию.

В прошлом году я заметил, что часто приложение использует только около 1 ГБ, прежде чем оно уже исчерпывает память. После некоторых исследований выяснилось, что причиной этой проблемы является фрагментация памяти. Я использовал VMMAP (утилиту SysInternals), чтобы посмотреть на использование памяти моим приложением, и увидел что-то вроде этого: Address Space Fragmentation

Оранжевые области - это память, выделенная моим приложением. Фиолетовые области - это исполняемый код.

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

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

Вопросы:

  • Как я могу влиять на место, где DLL загружаются в память, без явного использования REBASE для всех DLL на компьютере клиента или без явной загрузки всех DLL.
  • Есть ли способ указать адреса загрузки DLL в вашем файле манифеста приложения?
  • Или есть способ указать Windows (через файл манифеста?) Не разбрасывать библиотеки DLL вокруг (я думаю, что это рассеяние называется ASLR).

Конечно, лучшее решение - это то, на которое я могу влиять из файла манифеста моего приложения, поскольку я полагаюсь на автоматическую / динамическую загрузку DLL-файлов Windows.

Мое приложение является приложением смешанного режима (управляемое + неуправляемое), хотя основная часть приложения неуправляемая.

Есть предложения?

Ответы [ 3 ]

5 голосов
/ 17 ноября 2011

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

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

В AFAIK нет способа указать предпочтительный адрес сопоставления библиотеки DLL при ее загрузке. Так что есть только два варианта: либо перебазировать его (файл DLL), либо реализовать загрузку DLL самостоятельно (что, конечно, не тривиально).

Обычно вам не нужно перебазировать стандартные библиотеки DLL API Windows, они очень плотно загружаются в ваше адресное пространство. Фрагментация может поступать из некоторых сторонних DLL (таких как хуки Windows, антивирусные инъекции и т. Д.)

3 голосов
/ 17 ноября 2011

Вы не можете сделать это с манифестом, это должно быть сделано с помощью опции компоновщика / BASE. Линкер + Расширенный + Базовый адрес в IDE. Наиболее гибкий способ - использовать / BASE: @ filename, синтаксис ключа, чтобы компоновщик считывал базовый адрес из текстового файла.

Лучший способ заполнить текстовый файл - это окно Debug + Windows + Modules. Загрузите сборку Release вашей программы в отладчик и загрузите весь shebang. Debug + Break All, откройте окно и скопируйте его в текстовый файл. Измените его в соответствии с требуемым форматом, рассчитав адреса загрузки из столбца «Адрес». Оставьте достаточно места между библиотеками DLL, чтобы вам не приходилось постоянно настраивать текстовый файл.

1 голос
/ 18 ноября 2011

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

В противном случае вам потребуетсяопределить ответственные библиотеки DLL, определить, почему они загружаются.Например, являются ли они частью .NET, библиотеки времени выполнения языка, вашего собственного кода или сторонних библиотек, которые вы используете?

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

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

Я не знаю, можете ли вы что-нибудь сделать с библиотеками .NET;но поскольку большая часть вашего кода неуправляема, возможно, удастся устранить управляемые компоненты, чтобы избавиться от .NET.Или, возможно, вы могли бы разделить части .NET на отдельный процесс.

...