Сначала вы можете приостановить G C, позвонив по номеру System.GC.TryStartNoGCRegion
и отменить его с System.GC.EndNoGCRegion
.
Только для того, чтобы знать, сколько байтов выделено, есть System.GC.GetAllocatedBytesForCurrentThread
, который возвращает общее количество байтов, выделенных для текущего потока. Вызывайте его до и после кода для измерения, и разница заключается в размере выделения.
Подсчет количества выделений немного сложнее. Есть, возможно, немало способов сделать это, которые сегодня все-таки неоптимальны. Я могу придумать одну идею:
Изменение стандартного G C
Начиная с. NET Core 2.1 есть возможность использовать пользовательский G C, так называемый местный G C. Говорят, что опыт разработки, документация и полезность не самые лучшие, но в зависимости от деталей вашей проблемы это может быть полезно для вас.
Каждый раз, когда объект выделяется, среда выполнения вызывает Object* IGCHeap::Alloc(gc_alloc_context * acontext, size_t size, uint32_t flags)
. IGCHeap
определено здесь со стандартной реализацией G C здесь (GCHeap :: Allo c реализовано в строке 37292).
Парень в поговорить здесь будет Конрад Кокоса с двумя презентациями на эту тему c: # 1 , # 2 , слайды .
Мы можем принять реализацию G C по умолчанию как есть и изменить Alloc
-метод для увеличения счетчика при каждом вызове.
Предоставление счетчика в управляемом коде
Далее, чтобы использовать новый счетчик, нам нужен способ использовать его из управляемого кода. Для этого нам нужно изменить время выполнения. Здесь я опишу, как это сделать, расширив интерфейс G C (представлен System.GC
).
Примечание: у меня нет практического опыта в этом, и, вероятно, есть некоторые проблемы, возникающие при прохождении этого маршрута. Я просто хочу быть точным с моей идеей.
Взглянув на ulong GC.GetGenerationSize(int)
, мы можем выяснить, как добавить метод, который приводит к внутреннему вызову CLR.
Откройте \ runtime \ src \ coreclr \ src \ System.Private.CoreLib \ src \ System \ G C .cs # 112 и объявите новый метод:
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern ulong GetAllocationCount();
Далее нам нужно определить этот метод на собственном GCInterface. Для этого нужно получить runtime \ src \ coreclr \ src \ vm \ comutilnative.h # 112 и добавить:
static FCDECL0(UINT64, GetAllocationCount);
Чтобы связать эти два метода, нам нужно перечислить их в runtime \ src \ coreclr \ src \ vm \ ecalllist.h # 745 :
FCFuncElement("GetAllocationCount", GCInterface::GetAllocationCount)
И, наконец, фактически реализует метод в runtime \ src \ coreclr \ src \ vm \ comutilnative.cpp # 938 :
FCIMPL0(UINT64, GCInterface::GetAllocationCount)
{
FCALL_CONTRACT;
return (UINT64)(GCHeapUtilities::GetGCHeap()->GetAllocationCount());
}
FCIMPLEND
Получил бы указатель на GCHeap, где живет наш счетчик выделения. Метод GetAllocationCount
, который предоставляет это, еще не существует, поэтому давайте создадим его:
runtime \ src \ coreclr \ src \ gc \ gcimpl.h # 313
size_t GetAllocationCount();
runtime \ src \ coreclr \ src \ gc \ gcinterface.h # 680
virtual size_t GetAllocationCount() = 0;
runtime \ src \ coreclr \ src \ gc \ gcee .cpp # 239
size_t GCHeap::GetAllocationCount()
{
return m_ourAllocationCounter;
}
Чтобы наш новый метод System.GC.GetAllocationCount()
был пригоден для использования в управляемом коде, нам нужно скомпилировать его в соответствии с пользовательским BCL. Возможно, здесь также будет работать пользовательский пакет NuGet (который определяет System.GC.GetAllocationCount()
как внутренний вызов, как показано выше).
Закрытие
По общему признанию, это было бы совсем немного работы, если бы не было сделано раньше и пользовательский G C + CLR мог бы быть здесь немного излишним, но я подумал, что я должен выбросить его туда как возможность.
Кроме того, я не проверял это. Вы должны принять это как концепцию.