Упражнение 2. Отслеживание распределения процессов в пользовательском режиме
Выделения кучи выполняются непосредственно через API-интерфейсы кучи (HeapAlloc, HeapRealloc и выделения C/C++, такие как new, alloc, realloc, calloc) и обслуживаются с помощью трех типов кучи:
Основная куча NT — запросы на выделение служб размером менее 64 КБ.
Куча с низкой фрагментацией — состоит из подсементов, которые запрашивают выделение служб блоками фиксированного размера.
VirtualAlloc — запросы на выделение служб размером более 64 КБ.
VirtualAlloc используется для больших динамических выделений памяти, которые выполняются непосредственно через API VirtualAlloc . Обычно используется для растровых изображений или буферов. С помощью VirtualAlloc можно зарезервировать блок страниц, а затем выполнить дополнительные вызовы VirtualAlloc для фиксации отдельных страниц из зарезервированного блока. Это позволяет процессу резервировать диапазон своего виртуального адресного пространства без использования физического хранилища до тех пор, пока он не потребуется.
В этой области необходимо разобраться в двух понятиях:
Зарезервированная память: резервирует диапазон адресов для использования, но не получает ресурсы памяти.
Зафиксированная память. Гарантирует, что при наличии ссылок на адреса будет доступно физическое пространство памяти или файла подкачки.
В этом упражнении вы узнаете, как собирать трассировки, чтобы выяснить, как процесс пользовательского режима выделяет память.
В этом упражнении основное внимание уделяется фиктивным процессам тестирования MemoryTestApp.exe , который выделяет память с помощью:
API VirtualAlloc для фиксации буферов большой памяти.
Оператор C++ new для создания экземпляров небольших объектов.
Вы можете скачать MemoryTestApp.exeздесь.
Шаг 1. Сбор трассировки виртуального экземпляра и кучи с помощью WPR
Выделение больших объемов памяти обычно влияет на объем процесса и обслуживается API VirtualAlloc . Именно здесь должны начинаться все исследования, но также возможно, что процесс будет неправильно вести себя с меньшими выделениями (например, утечки памяти с помощью нового оператора в C++ и т. д.). Трассировка кучи становится полезной в этой ситуации.
Шаг 1.1. Подготовка системы к трассировке кучи
Трассировку кучи следует рассматривать как необязательную и выполнять, если анализ VirtualAlloc не предоставляет соответствующего объяснения проблемы с использованием памяти. Трассировка кучи, как правило, создает более крупные трассировки, и рекомендуется включать трассировку только для отдельных процессов, которые вы изучаете.
Добавьте раздел реестра для интересующего процесса ( в данном случаеMemoryTestApp.exe); Затем трассировка кучи включается для каждого последующего создания процесса.
reg add "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MemoryTestApp.exe" /v TracingFlags /t REG_DWORD /d 1 /f
Шаг 1.2. Запись трассировки с помощью WPR
На этом шаге вы соберете трассировку с помощью WPR , которая содержит данные VirtualAlloc и Heap .
Откройте WPR и измените конфигурацию трассировки.
Выберите поставщики VirtualAlloc и Heap .
Выберите общийв качестве сценария производительности.
Выберите общийрежим ведения журнала.
Нажмите кнопку Пуск , чтобы начать трассировку.
Запустите MemoryTestApp.exeи дождитесь завершения процесса (это займет около 30 секунд).
Вернитесь в WPR, сохраните трассировку и откройте ее с помощью Windows Анализатор производительности (WPA).
Откройте меню Трассировка и выберите Настроить путь к символам.
- Укажите путь к кэшу символов. Дополнительные сведения о символах см. на странице Поддержка символов на сайте MSDN.
Откройте меню Трассировка и выберите Загрузить символы.
Теперь у вас есть трассировка, которая содержит все шаблоны выделения памяти для процессаMemoryTestApp.exe в течение его жизненного цикла.
Шаг 2. Проверка динамических выделений VirtualAlloc
Подробные данные VirtualAlloc предоставляются с помощью графа "Время существования фиксации VirtualAlloc" в WPA. Ниже перечислены ключевые столбцы, представляющие интерес.
Столбец | Описание |
---|---|
Process | Имя процесса, выполняющего выделение памяти через VirtualAlloc. |
Стек фиксации | Стек вызовов, показывающий путь кода, ведущий к выделению памяти. |
Время фиксации | Метка времени выделения памяти. |
Время списания | Метка времени освобождения памяти. |
Влияющий размер | Размер невыполненных выделений или разница в размере между началом и окончанием выбранного интервала времени. Этот размер изменяется в зависимости от выбранного порта представления. Значение "Влияющий размер" будет равным нулю, если вся память, выделенная процессом, освобождается к концу визуализированного интервала в WPA. |
Размер | Совокупная сумма всех выделений за выбранный интервал времени. |
Выполните следующие действия для анализа MemoryTestApp.exe
Найдите граф Время существования фиксации VirtualAlloc в категории ПамятьОбозреватель графа.
Перетащите время существования фиксации VirtualAlloc на вкладку Анализ .
Упорядочение таблицы для отображения этих столбцов. Щелкните правой кнопкой мыши заголовки столбцов, чтобы добавить или удалить столбцы.
Процесс
Влияющий тип
Стек фиксации
Время фиксации и время списания
Count
Влияние на размер и размер
Найдите MemoryTestApp.exe в списке процессов.
Примените фильтр, чтобы сохранить на графе только MemoryTestApp.exe .
- Щелкните правой кнопкой мыши и выберите Фильтр для выбора.
Окно просмотра анализа должно выглядеть примерно так:
В предыдущем примере представляют интерес два значения:
Размер 126 МБ. Это означает, что MemoryTestApp.exe выделено в общей сложности 125 МБ в течение срока существования. Он представляет совокупную сумму всех вызовов API VirtualAlloc , выполняемых процессом, и его зависимостей.
Влияет на размер 0 МБ. Это означает, что вся память, выделенная процессом, освобождается к концу текущего анализируемого интервала времени. Система не пострадала от увеличения использования памяти в устойчивом состоянии.
Шаг 2.1. Анализ использования памяти в устойчивом состоянии
При изучении выделения памяти следует попытаться ответить на вопрос: "Почему для этого сценария растет использование памяти в устойчивом состоянии?" В MemoryTestApp.exe примере видно, что в начале выделено около 10 МБ памяти устойчивого состояния, а затем она увеличивается до 20 МБ на полпути.
Чтобы исследовать это поведение, сузьте масштаб до интервала времени, когда внезапное увеличение происходит в середине трассировки.
Окно просмотра должно выглядеть следующим образом.
Как видите, размер влияющего значения теперь составляет 10 МБ. Это означает, что между началом и концом анализируемого интервала времени наблюдается увеличение использования памяти в устойчивом состоянии на 10 МБ.
Сортировать по влиянию на размер , щелкнув заголовок столбца.
Разверните строкуMemoryTestApp.exe (в столбце Процесс ).
Разверните строку Влияющая (в столбце "Влияющий тип ").
Перейдите по процессу Commit Stack , пока не найдете функцию, которая выделила 10 МБ памяти.
В этом примере функция MainMemoryTestApp.exe выделяет 10 МБ памяти в середине рабочей нагрузки путем прямого вызова VirtualAlloc. В реальном мире разработчик приложения должен определить, является ли выделение разумным или можно ли изменить порядок кода, чтобы свести к минимуму увеличение использования памяти в устойчивом состоянии.
Теперь вы можете удалить окно просмотра в WPA.
Шаг 2.2. Анализ временного (или пикового) использования памяти
При изучении выделения памяти следует попытаться ответить на вопрос: "Почему для этой части сценария наблюдается временный пик использования памяти?" Временные выделения вызывают пики использования памяти и могут привести к фрагментации и отправке ценного содержимого из системного резервного кэша при нехватке памяти.
В примере MemoryTest видно, что в трассировке равномерно распределено 10 разных пиков использования памяти (10 МБ).
Сузьте масштаб до последних четырех пиков, чтобы сосредоточиться на меньшей интересующей области и уменьшить шум от несоответствующих действий.
Окно просмотра должно выглядеть следующим образом:
Выполните сортировку по размеру , щелкнув заголовок столбца.
Разверните строкуMemoryTestApp.exe (в столбце Процесс ).
Щелкните временную строку (в столбце "Влияющий тип ").
- Он должен выделять синим цветом все пики использования памяти в окне просмотра.
Обратите внимание на значения различных столбцов:
Count = 4. Это означает, что в течение этого интервала было выполнено четыре временных выделения памяти.
Влияющий размер = 0 МБ. Это означает, что все четыре временных выделения памяти были освобождены к концу интервала времени.
Размер = 40 МБ. Это означает, что сумма всех четырех временных выделений памяти составляет 40 МБ памяти.
Перейдите по процессу Фиксация стека , пока не найдете функции, которым выделено 40 МБ памяти.
В этом примере функция MainMemoryTestApp.exe вызывает функцию с именем Operation1, которая, в свою очередь, вызывает функцию с именем МанипулТемпорныйBuffer. Эта функция ManipulateTemporaryBuffer затем напрямую вызывает VirtualAlloc четыре раза, создавая и освобождая буфер памяти размером 10 МБ каждый раз. Буферы хранятся только по 100 мс каждый. Выделение буферов и свободное время представлены столбцами Время фиксации и Время списания .
В реальном мире разработчик приложения определит, необходимы ли эти временные временные буферные выделения или их можно заменить с помощью постоянного буфера памяти для операции.
Теперь вы можете удалить окно просмотра в WPA.
Шаг 3. Проверка динамических выделений кучи
До сих пор анализ был сосредоточен только на больших выделениях памяти, которые обслуживаются API VirtualAlloc . Следующим шагом является определение проблем с другими небольшими выделениями, сделанными процессом, с помощью данных кучи, изначально собранных.
Подробные данные кучи предоставляются с помощью графа "Выделения кучи" в WPA. Ниже перечислены ключевые столбцы, представляющие интерес.
Столбец | Описание |
---|---|
Process | Имя процесса, выполняющего выделение памяти. |
Дескриптор | Идентификатор кучи, используемой для обслуживания выделения. Можно создавать кучи, поэтому для процесса может быть несколько дескрипторов кучи. |
Стек | Стек вызовов, показывающий путь кода, который приводит к выделению памяти. |
Время выделения | Метка времени выделения памяти. |
Влияющий размер | Размер невыполненных выделений или разница между началом и концом выбранного окна просмотра. Этот размер изменяется в зависимости от выбранного интервала времени. |
Размер | Совокупная сумма всех выделений или освобождений. |
Выполните следующие действия для анализа MemoryTestApp.exe
Найдите граф Выделения кучи в категории ПамятьОбозреватель графа.
Перетащите выделение кучи на вкладку Анализ .
Упорядочение таблицы для отображения следующих столбцов:
Процесс
Дескриптор
Влияющий тип
Стек
AllocTime
Count
Влияние на размер и размер
Найдите MemoryTestApp.exe в списке процессов.
Примените фильтр, чтобы сохранить на графе только MemoryTestApp.exe .
- Щелкните правой кнопкой мыши и выберите Пункт Отфильтровать .
Окно просмотра должно выглядеть следующим образом:
В этом примере видно, что размер одной из кучи постоянно увеличивается с течением времени с постоянной скоростью. В этой куче выделено 1200 памяти, что на конец интервала приходится 130 КБ используемой памяти.
Увеличение масштаба с меньшим интервалом (например, 10 секунд) в середине трассировки.
Разверните главный дескриптор , который показывает наибольшее количество выделений (как показано в столбце "Влияющий размер ").
Разверните тип Влияющее .
Перемещайтесь по стеку процессов, пока не найдете функцию, которая отвечает за выделение всей памяти.
В этом примере функция MainMemoryTestApp.exe вызывает функцию с именем InnerLoopOperation. Затем эта функция InnerLoopOperation выделяет 40 байт памяти 319 раз с помощью нового оператора C++. Эта память остается выделенной до завершения процесса.
В реальном мире разработчик приложения должен определить, подразумевает ли такое поведение возможную утечку памяти, и устранить проблему.
Шаг 4. Очистка тестовой системы
После завершения анализа необходимо очистить реестр, чтобы убедиться, что трассировка кучи отключена для процесса. Выполните следующую команду в командной строке с повышенными привилегиями:
reg delete "HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\MemoryTestApp.exe" /v TracingFlags /f