События пользовательской собственной кучи трассировки событий Windows
Visual Studio содержит разнообразные средства профилирования и диагностики, включая встроенный профилировщик памяти. Этот профилировщик обрабатывает события ETW от поставщика кучи и анализирует выделение и использование памяти. По умолчанию это средство может анализировать только выделения из стандартной кучи Windows, а выделения вне собственной кучи не отображаются.
Во многих случаях может потребоваться использовать пользовательскую кучу и исключить издержки при распределении из стандартной кучи. Например, можно использовать VirtualAlloc, чтобы выделить большой объем памяти при запуске приложения или игры, а затем управлять собственными блоками в рамках этого списка. В этом случае средство профилирования памяти будет видеть только первоначальное выделение, а не пользовательское управление, выполняемые в блоке памяти. Но с помощью поставщика ETW пользовательской собственной кучи можно указать средству на выделение, осуществляемые вне стандартной кучи.
Например, в проекте, аналогичном следующему, где MemoryPool
является пользовательской кучей, будет доступно только одно выделение в куче Windows.
class Foo
{
public:
int x, y;
};
...
// MemoryPool is a custom managed heap, which allocates 8192 bytes
// on the standard Windows Heap named "Windows NT"
MemoryPool<Foo, 8192> mPool;
// the "allocate" method requests memory from the pool created above
// and is cast to an object of type Foo, shown above
Foo* pFoo1 = (Foo*)mPool.allocate();
Foo* pFoo2 = (Foo*)mPool.allocate();
Foo* pFoo3 = (Foo*)mPool.allocate();
Моментальный снимок из средства Использование памяти без отслеживания пользовательской кучи будет отображать только одно 8192-байтовое распределение и ни одного из пользовательских распределений, сделанных пулом:
Выполнив следующие действия, можно использовать это же средство для отслеживания использования памяти в пользовательской куче.
Использование
Эту библиотеку можно легко использовать в C и C++.
Включите заголовок для поставщика ETW пользовательской кучи:
#include <VSCustomNativeHeapEtwProvider.h>
Добавьте декоратор
__declspec(allocator)
к любой функции в диспетчере пользовательской кучи, которая вернет указатель на только что выделенную память кучи. Это декоратора позволяет средству правильно определять возвращаемый тип памяти. Рассмотрим пример.__declspec(allocator) void *MyMalloc(size_t size);
Примечание.
Этот декоратор сообщит компилятору, что данная функция представляет собой вызов распределителя. В результате каждого вызова функции будет выводиться адрес места вызова, размер инструкции вызова и идентификатор типа нового объекта для нового символа
S_HEAPALLOCSITE
. После выделения стека вызовов Windows выдаст событие ETW с этими сведениями. Средство профилирования памяти просматривает стек вызовов, чтобы найти обратный адрес, соответствующий символуS_HEAPALLOCSITE
. Сведения об ИД типа в символе используются для отображения типа среды выполнения для выделения.Это означает, что вызов, который имеет вид
(B*)(A*)MyMalloc(sizeof(B))
, будет отображаться в средстве как имеющий типаB
, а неvoid
илиA
.Для C++ создайте объект
VSHeapTracker::CHeapTracker
, указав имя для кучи, которое будет отображаться в средстве профилирования.auto pHeapTracker = std::make_unique<VSHeapTracker::CHeapTracker>("MyCustomHeap");
Если вы работаете с C, используйте функцию
OpenHeapTracker
. Эта функция возвращает дескриптор, который будет использоваться при вызове других функций отслеживания.VSHeapTrackerHandle hHeapTracker = OpenHeapTracker("MyHeap");
При выделении памяти с помощью настраиваемой функции вызовите метод
AllocateEvent
(C++) илиVSHeapTrackerAllocateEvent
(C), передав указатель на память и ее размер, чтобы отслеживать выделение.pHeapTracker->AllocateEvent(memPtr, size);
or
VSHeapTrackerAllocateEvent(hHeapTracker, memPtr, size);
Важно!
Не забудьте пометить пользовательскую функцию распределителя декоратором
__declspec(allocator)
, описанным выше.При освобождении памяти с помощью настраиваемой функции вызовите метод
DeallocateEvent
(C++) илиVSHeapTracerDeallocateEvent
(C), передав указатель на память и ее размер, чтобы отслеживать выделение.pHeapTracker->DeallocateEvent(memPtr);
или:
VSHeapTrackerDeallocateEvent(hHeapTracker, memPtr);
При перераспределении памяти с помощью настраиваемой функции вызовите метод
ReallocateEvent
(C++) илиVSHeapReallocateEvent
(C), передав указатель на новую память, размер выделения и указатель на старую память.pHeapTracker->ReallocateEvent(memPtrNew, size, memPtrOld);
или:
VSHeapTrackerReallocateEvent(hHeapTracker, memPtrNew, size, memPtrOld);
И, наконец, чтобы закрыть и очистить средство отслеживания пользовательской кучи в C++, используйте деструктор
CHeapTracker
вручную или с помощью стандартных правила области видимости либо используйте функциюCloseHeapTracker
в C.delete pHeapTracker;
или:
CloseHeapTracker(hHeapTracker);
Отслеживание использования памяти
При наличии этих вызовов для отслеживания использования пользовательской кучи теперь можно применять стандартное средство Использование памяти в Visual Studio. Дополнительные сведения об использовании этого средства см. в документации по средству Использование памяти. Убедитесь, что вы включили профилирование кучи с моментальными снимками, в противном случае использование пользовательской кучи отображаться не будет.
Чтобы просмотреть отслеживание пользовательской кучи, воспользуйтесь раскрывающимся списком Куча, расположенным в правом верхнем углу окна Моментальный снимок, для смены представления с Куча NT на вашу собственную кучу.
Используя приведенный выше пример кода с MemoryPool
для создания объекта VSHeapTracker::CHeapTracker
и собственным методом allocate
, вызывающим метод AllocateEvent
, теперь можно увидеть результат пользовательского выделения — три экземпляра общим размером 24 байта с типом Foo
.
Куча NT по умолчанию выглядит так же, как и ранее, но к ней добавлен объект CHeapTracker
.
Как и для стандартной кучи Windows, это средство можно использовать для сравнения моментальных снимков и поиска утечек и повреждений в пользовательской куче, как описывается в основной документации по средству Использование памяти.
Совет
Visual Studio также содержит средство Использование памяти в наборе инструментов Профилирование производительности, который доступен при выборе пунктов Отладка>Профилировщик производительности или при нажатии сочетания клавиш Alt+F2. Эта функция не поддерживает отслеживание кучи и не отображает пользовательскую кучу, как описано здесь. Эта возможность доступна только в диалоговом окне Средства диагностики, которое можно открыть, последовательно выбрав Отладка>Окна>Показать средства диагностики либо нажав сочетание клавиш Ctrl+Alt+F2.
Связанный контент
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по