Я бы хотел рассчитать пиковое потребление памяти моего приложения. Для этого я хотел бы отслеживать распределение и освобождение. Ниже приводится краткое описание концепции. Я надеюсь, что вы можете подтвердить / исправить это.
Мониторинг распределения
Все макросы-распределители (GC_MALLOC
, GC_REALLOC
и т. Д.) Перехватываются, чтобы сохранить для указанных указателей их первоначальный размер (запрошенный размер, а не GC_size).
Пример:
// Track all the pointers with the size.
// The size is needed to know when free happens.
std::map<void*, size_t> addressTable;
size_t allocated = 0;
size_t peak_allocated = 0;
#undef GC_MALLOC
#define GC_MALLOC(size) GC_malloc_hook(size)
void* GC_malloc_hook(size_t size)
{
#if defined(GC_DEBUG)
void* ptr = GC_debug_malloc(size, GC_EXTRAS);
#else
void* ptr = GC_malloc(size);
#endif
addressTable[ptr] = size; // Save the pointer and its size.
allocated += size;
if (allocated > peak_allocated)
peak_allocated = allocated;
return ptr;
}
Контроль выделения ресурсов
Есть два разных решения для этой части. Я не знаю, эквивалентны ли они как возможные решения для проверки освобождения.
Первое решение (на основе обратных вызовов финализатора)
Используйте макрос GC_REGISTER_FINALIZER_NO_ORDER
, чтобы зарегистрировать обратный вызов финализатора для каждого указателя (предоставленного GC_MALLOC
и его друзьями). В этом случае GC может уведомить об освобождении области памяти.
Обратите внимание, что GC_FREE
также следует проверять, потому что (как я проверял) обратные вызовы финализатора не будут выполняться, когда происходит явное освобождение.
Пример (дополнить приведенный выше код обратным вызовом финализатора):
void* GC_malloc_hook(size_t size)
{
#if defined(GC_DEBUG)
void* ptr = GC_debug_malloc(size, GC_EXTRAS);
#else
void* ptr = GC_malloc(size);
#endif
addressTable[ptr] = size; // Save the pointer and its size.
allocated += size;
if (allocated > peak_allocated)
peak_allocated = allocated;
GC_REGISTER_FINALIZER_NO_ORDER(ptr, [](void* obj, void* cd)
{
allocated -= addressTable[obj];
addressTable.erase(obj);
}, nullptr, nullptr, nullptr);
return ptr;
}
#undef GC_FREE
#define GC_FREE(address) GC_free_hook(address)
void GC_free_hook(void* address)
{
#if defined(GC_DEBUG)
GC_debug_free(address);
#else
GC_free(address);
#endif
allocated -= addressTable[address];
addressTable.erase(address);
}
Второе решение (на основе события сбора GC)
Когда генерируется событие GC_EVENT_END
(предположим, что произошло некоторое свободное), отслеживаемые указатели должны быть проверены, если они все еще существуют в куче GC. Я не уверен, но, возможно, функция GC_is_valid_displacement
может помочь в этом.
// The pointer is not valid.
GC_is_valid_displacement_print_proc = [](void* ptr)
{
allocated -= addressTable[ptr];
addressTable.erase(ptr);
};
GC_set_on_collection_event([](GC_EventType evtType)
{
if (GC_EVENT_END != evtType)
return;
// Loop on all the tracked pointers and check their validity after GC.
auto iter = addressTable.begin();
while (iter != addressTable.end()) {
GC_is_valid_displacement(iter->first);
iter++;
}
});
Что ты думаешь? Какое лучшее решение проверить, происходит ли освобождение памяти?
Время ГХ (когда происходит финализация ГХ) влияет на значение пиковой памяти. Таким образом, различные пиковые значения возможны при многократном запуске моего приложения. Есть ли какая-либо особенность в BDWGC, которая помогает запускать финализаторы GC одновременно (чтобы мое приложение всегда получало одинаковое пиковое значение с покрытием)?