From caf692387fc53e835f3bff48abb8328c941f858e Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Fri, 8 May 2020 21:39:46 -0700 Subject: [PATCH] Finish the other part of the GCTotalPhysicalMemory config implementation (#36152) * Finish the other part of the GCTotalPhysicalMemory config implementation The total physical memory specified by the GCTotalPhysicalMemory config needs to communicated over to the VM side for memory load calculation. Otherwise inside GC we are using the correct physical memory specified, but when we ask for the memory load it's still based on the total available memory. * Delete unnecessary delay-loading of kernel32.dll * Avoid mutable restricted memory limit inside OS PAL Co-authored-by: Maoni0 --- src/coreclr/src/gc/env/gcenv.os.h | 7 +- src/coreclr/src/gc/gc.cpp | 15 ++-- src/coreclr/src/gc/gcpriv.h | 3 + src/coreclr/src/gc/unix/gcenv.unix.cpp | 19 ++--- src/coreclr/src/gc/windows/gcenv.windows.cpp | 78 ++++--------------- src/coreclr/src/vm/gcenv.os.cpp | 82 ++++---------------- 6 files changed, 56 insertions(+), 148 deletions(-) diff --git a/src/coreclr/src/gc/env/gcenv.os.h b/src/coreclr/src/gc/env/gcenv.os.h index ae398e5503e3b..a2d837a82d50d 100644 --- a/src/coreclr/src/gc/env/gcenv.os.h +++ b/src/coreclr/src/gc/env/gcenv.os.h @@ -433,20 +433,19 @@ class GCToOSInterface // Remarks: // If a process runs with a restricted memory limit, it returns the limit. If there's no limit // specified, it returns amount of actual physical memory. - // - // PERF TODO: Requires more work to not treat the restricted case to be special. - // To be removed before 3.0 ships. static uint64_t GetPhysicalMemoryLimit(bool* is_restricted=NULL); // Get memory status // Parameters: + // restricted_limit - The amount of physical memory in bytes that the current process is being restricted to. If non-zero, it used to calculate + // memory_load and available_physical. If zero, memory_load and available_physical is calculate based on all available memory. // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory // that is in use (0 indicates no memory use and 100 indicates full memory use). // available_physical - The amount of physical memory currently available, in bytes. // available_page_file - The maximum amount of memory the current process can commit, in bytes. // Remarks: // Any parameter can be null. - static void GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file); + static void GetMemoryStatus(uint64_t restricted_limit, uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file); // Get size of an OS memory page static size_t GetPageSize(); diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index a91de0d388d5e..c09b2c0182f26 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -2175,6 +2175,8 @@ uint32_t gc_heap::m_high_memory_load_th; uint32_t gc_heap::v_high_memory_load_th; +bool gc_heap::is_restricted_physical_mem; + uint64_t gc_heap::total_physical_mem = 0; uint64_t gc_heap::entry_available_physical_mem = 0; @@ -19628,7 +19630,7 @@ void gc_heap::get_memory_info (uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { - GCToOSInterface::GetMemoryStatus(memory_load, available_physical, available_page_file); + GCToOSInterface::GetMemoryStatus(is_restricted_physical_mem ? total_physical_mem : 0, memory_load, available_physical, available_page_file); } void fire_mark_event (int heap_num, int root_type, size_t bytes_marked) @@ -34925,11 +34927,14 @@ HRESULT GCHeap::Initialize() g_num_processors = GCToOSInterface::GetTotalProcessorCount(); assert(g_num_processors != 0); - bool is_restricted; gc_heap::total_physical_mem = (size_t)GCConfig::GetGCTotalPhysicalMemory(); - if (!(gc_heap::total_physical_mem)) + if (gc_heap::total_physical_mem != 0) + { + gc_heap::is_restricted_physical_mem = true; + } + else { - gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&is_restricted); + gc_heap::total_physical_mem = GCToOSInterface::GetPhysicalMemoryLimit (&gc_heap::is_restricted_physical_mem); } #ifdef HOST_64BIT @@ -34948,7 +34953,7 @@ HRESULT GCHeap::Initialize() // running in a container, use this limit for the GC heap. if (!(gc_heap::heap_hard_limit)) { - if (is_restricted) + if (gc_heap::is_restricted_physical_mem) { uint64_t physical_mem_for_gc = gc_heap::total_physical_mem * (uint64_t)75 / (uint64_t)100; gc_heap::heap_hard_limit = (size_t)max ((20 * 1024 * 1024), physical_mem_for_gc); diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index 3bcb383870773..e3a6c1f5b60ab 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -3289,6 +3289,9 @@ class gc_heap PER_HEAP_ISOLATED uint32_t v_high_memory_load_th; + PER_HEAP_ISOLATED + bool is_restricted_physical_mem; + PER_HEAP_ISOLATED uint64_t mem_one_percent; diff --git a/src/coreclr/src/gc/unix/gcenv.unix.cpp b/src/coreclr/src/gc/unix/gcenv.unix.cpp index fd3cd5ec91b6d..9d074231a690d 100644 --- a/src/coreclr/src/gc/unix/gcenv.unix.cpp +++ b/src/coreclr/src/gc/unix/gcenv.unix.cpp @@ -957,7 +957,7 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount() // Return the size of the user-mode portion of the virtual address space of this process. // Return: -// non zero if it has succeeded, 0 if it has failed +// non zero if it has succeeded, (size_t)-1 if not available size_t GCToOSInterface::GetVirtualMemoryLimit() { #ifdef HOST_64BIT @@ -1143,11 +1143,13 @@ uint64_t GetAvailablePageFile() // Get memory status // Parameters: +// restricted_limit - The amount of physical memory in bytes that the current process is being restricted to. If non-zero, it used to calculate +// memory_load and available_physical. If zero, memory_load and available_physical is calculate based on all available memory. // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory // that is in use (0 indicates no memory use and 100 indicates full memory use). // available_physical - The amount of physical memory currently available, in bytes. // available_page_file - The maximum amount of memory the current process can commit, in bytes. -void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) +void GCToOSInterface::GetMemoryStatus(uint64_t restricted_limit, uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { uint64_t available = 0; uint32_t load = 0; @@ -1155,16 +1157,14 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available if (memory_load != nullptr || available_physical != nullptr) { size_t used; - bool isRestricted; - uint64_t total = GetPhysicalMemoryLimit(&isRestricted); - if (isRestricted) + if (restricted_limit != 0) { // Get the physical memory in use - from it, we can get the physical memory available. // We do this only when we have the total physical memory available. if (GetPhysicalMemoryUsed(&used)) { - available = total > used ? total-used : 0; - load = (uint32_t)(((float)used * 100) / (float)total); + available = restricted_limit > used ? restricted_limit - used : 0; + load = (uint32_t)(((float)used * 100) / (float)restricted_limit); } } else @@ -1173,7 +1173,9 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available if (memory_load != NULL) { - uint32_t load = 0; + bool isRestricted; + uint64_t total = GetPhysicalMemoryLimit(&isRestricted); + if (total > available) { used = total - available; @@ -1191,7 +1193,6 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available if (available_page_file != nullptr) *available_page_file = GetAvailablePageFile(); - } // Get a high precision performance counter diff --git a/src/coreclr/src/gc/windows/gcenv.windows.cpp b/src/coreclr/src/gc/windows/gcenv.windows.cpp index 2aaf7ba19aa5e..a9504bd4b69ad 100644 --- a/src/coreclr/src/gc/windows/gcenv.windows.cpp +++ b/src/coreclr/src/gc/windows/gcenv.windows.cpp @@ -18,22 +18,12 @@ GCSystemInfo g_SystemInfo; -typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb); -static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0; - static size_t g_RestrictedPhysicalMemoryLimit = (size_t)UINTPTR_MAX; -// For 32-bit processes the virtual address range could be smaller than the amount of physical -// memory on the machine/in the container, we need to restrict by the VM. -static bool g_UseRestrictedVirtualMemory = false; - static bool g_SeLockMemoryPrivilegeAcquired = false; static AffinitySet g_processAffinitySet; -typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result); -typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength); - namespace { static bool g_fEnableGCNumaAware; @@ -297,36 +287,14 @@ static size_t GetRestrictedPhysicalMemoryLimit() uint64_t total_virtual = 0; uint64_t total_physical = 0; BOOL in_job_p = FALSE; - HINSTANCE hinstKernel32 = 0; - - PIS_PROCESS_IN_JOB GCIsProcessInJob = 0; - PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0; - - hinstKernel32 = LoadLibraryEx(L"kernel32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); - if (!hinstKernel32) - goto exit; - GCIsProcessInJob = (PIS_PROCESS_IN_JOB)GetProcAddress(hinstKernel32, "IsProcessInJob"); - if (!GCIsProcessInJob) - goto exit; - - if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) + if (!IsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) goto exit; if (in_job_p) { - GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo"); - - if (!GCGetProcessMemoryInfo) - goto exit; - - GCQueryInformationJobObject = (PQUERY_INFORMATION_JOB_OBJECT)GetProcAddress(hinstKernel32, "QueryInformationJobObject"); - - if (!GCQueryInformationJobObject) - goto exit; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; - if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, + if (QueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info), NULL)) { size_t job_memory_limit = (size_t)UINTPTR_MAX; @@ -373,13 +341,6 @@ static size_t GetRestrictedPhysicalMemoryLimit() if (job_physical_memory_limit == (size_t)UINTPTR_MAX) { job_physical_memory_limit = 0; - - if (hinstKernel32 != 0) - { - FreeLibrary(hinstKernel32); - hinstKernel32 = 0; - GCGetProcessMemoryInfo = 0; - } } // Check to see if we are limited by VM. @@ -399,15 +360,8 @@ static size_t GetRestrictedPhysicalMemoryLimit() if (total_virtual < total_physical) { - if (hinstKernel32 != 0) - { - // We can also free the lib here - if we are limited by VM we will not be calling - // GetProcessMemoryInfo. - FreeLibrary(hinstKernel32); - GCGetProcessMemoryInfo = 0; - } - g_UseRestrictedVirtualMemory = true; - job_physical_memory_limit = (size_t)total_virtual; + // Limited by virtual address space + job_physical_memory_limit = 0; } VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit); @@ -1044,7 +998,7 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount() // Return the size of the user-mode portion of the virtual address space of this process. // Return: -// non zero if it has succeeded, 0 if it has failed +// non zero if it has succeeded, (size_t)-1 if not available size_t GCToOSInterface::GetVirtualMemoryLimit() { MEMORYSTATUSEX memStatus; @@ -1067,7 +1021,7 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) { - if (is_restricted && !g_UseRestrictedVirtualMemory) + if (is_restricted) *is_restricted = true; return restricted_limit; @@ -1081,23 +1035,22 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) // Get memory status // Parameters: +// restricted_limit - The amount of physical memory in bytes that the current process is being restricted to. If non-zero, it used to calculate +// memory_load and available_physical. If zero, memory_load and available_physical is calculate based on all available memory. // memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory // that is in use (0 indicates no memory use and 100 indicates full memory use). // available_physical - The amount of physical memory currently available, in bytes. // available_page_file - The maximum amount of memory the current process can commit, in bytes. -void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) +void GCToOSInterface::GetMemoryStatus(uint64_t restricted_limit, uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { - uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) { size_t workingSetSize; BOOL status = FALSE; - if (!g_UseRestrictedVirtualMemory) - { - PROCESS_MEMORY_COUNTERS pmc; - status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - workingSetSize = pmc.WorkingSetSize; - } + + PROCESS_MEMORY_COUNTERS pmc; + status = GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + workingSetSize = pmc.WorkingSetSize; if(status) { @@ -1123,9 +1076,10 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available MEMORYSTATUSEX ms; ::GetProcessMemoryLoad(&ms); - if (g_UseRestrictedVirtualMemory) + // For 32-bit processes the virtual address range could be smaller than the amount of physical + // memory on the machine/in the container, we need to restrict by the VM. + if (ms.ullTotalVirtual < ms.ullTotalPhys) { - _ASSERTE (ms.ullTotalVirtual == restricted_limit); if (memory_load != NULL) *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual); if (available_physical != NULL) diff --git a/src/coreclr/src/vm/gcenv.os.cpp b/src/coreclr/src/vm/gcenv.os.cpp index 6b9e042f04e9b..e7add3b29e08b 100644 --- a/src/coreclr/src/vm/gcenv.os.cpp +++ b/src/coreclr/src/vm/gcenv.os.cpp @@ -756,7 +756,7 @@ uint32_t GCToOSInterface::GetCurrentProcessCpuCount() // Return the size of the user-mode portion of the virtual address space of this process. // Return: -// non zero if it has succeeded, 0 if it has failed +// non zero if it has succeeded, (size_t)-1 if not available size_t GCToOSInterface::GetVirtualMemoryLimit() { LIMITED_METHOD_CONTRACT; @@ -771,16 +771,6 @@ static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR; #ifndef TARGET_UNIX -// For 32-bit processes the virtual address range could be smaller than the amount of physical -// memory on the machine/in the container, we need to restrict by the VM. -static bool g_UseRestrictedVirtualMemory = false; - -typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb); -static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0; - -typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result); -typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength); - static size_t GetRestrictedPhysicalMemoryLimit() { LIMITED_METHOD_CONTRACT; @@ -793,34 +783,14 @@ static size_t GetRestrictedPhysicalMemoryLimit() uint64_t total_virtual = 0; uint64_t total_physical = 0; BOOL in_job_p = FALSE; - HINSTANCE hinstKernel32 = 0; - - PIS_PROCESS_IN_JOB GCIsProcessInJob = 0; - PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0; - - GCIsProcessInJob = &(::IsProcessInJob); - if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) + if (!IsProcessInJob(GetCurrentProcess(), NULL, &in_job_p)) goto exit; if (in_job_p) { - hinstKernel32 = WszLoadLibrary(L"kernel32.dll"); - if (!hinstKernel32) - goto exit; - - GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo"); - - if (!GCGetProcessMemoryInfo) - goto exit; - - GCQueryInformationJobObject = &(::QueryInformationJobObject); - - if (!GCQueryInformationJobObject) - goto exit; - JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info; - if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, + if (QueryInformationJobObject(NULL, JobObjectExtendedLimitInformation, &limit_info, sizeof(limit_info), NULL)) { size_t job_memory_limit = (size_t)MAX_PTR; @@ -867,13 +837,6 @@ static size_t GetRestrictedPhysicalMemoryLimit() if (job_physical_memory_limit == (size_t)MAX_PTR) { job_physical_memory_limit = 0; - - if (hinstKernel32 != 0) - { - FreeLibrary(hinstKernel32); - hinstKernel32 = 0; - GCGetProcessMemoryInfo = 0; - } } // Check to see if we are limited by VM. @@ -893,15 +856,8 @@ static size_t GetRestrictedPhysicalMemoryLimit() if (total_virtual < total_physical) { - if (hinstKernel32 != 0) - { - // We can also free the lib here - if we are limited by VM we will not be calling - // GetProcessMemoryInfo. - FreeLibrary(hinstKernel32); - GCGetProcessMemoryInfo = 0; - } - g_UseRestrictedVirtualMemory = true; - job_physical_memory_limit = (size_t)total_virtual; + // Limited by virtual address space + job_physical_memory_limit = 0; } VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit); @@ -928,9 +884,6 @@ static size_t GetRestrictedPhysicalMemoryLimit() // Get the physical memory that this process can use. // Return: // non zero if it has succeeded, 0 if it has failed -// -// PERF TODO: Requires more work to not treat the restricted case to be special. -// To be removed before 3.0 ships. uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) { LIMITED_METHOD_CONTRACT; @@ -941,11 +894,7 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) size_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) { - if (is_restricted -#ifndef TARGET_UNIX - && !g_UseRestrictedVirtualMemory -#endif - ) + if (is_restricted) *is_restricted = true; return restricted_limit; @@ -965,26 +914,22 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted) // available_page_file - The maximum amount of memory the current process can commit, in bytes. // Remarks: // Any parameter can be null. -void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) +void GCToOSInterface::GetMemoryStatus(uint64_t restricted_limit, uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file) { LIMITED_METHOD_CONTRACT; - uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit(); if (restricted_limit != 0) { size_t workingSetSize; BOOL status = FALSE; #ifndef TARGET_UNIX - if (!g_UseRestrictedVirtualMemory) - { - PROCESS_MEMORY_COUNTERS pmc; - status = GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); - workingSetSize = pmc.WorkingSetSize; - } + PROCESS_MEMORY_COUNTERS pmc; + status = GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + workingSetSize = pmc.WorkingSetSize; #else status = PAL_GetPhysicalMemoryUsed(&workingSetSize); #endif - if(status) + if (status) { if (memory_load) *memory_load = (uint32_t)((float)workingSetSize * 100.0 / (float)restricted_limit); @@ -1009,9 +954,10 @@ void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available GetProcessMemoryLoad(&ms); #ifndef TARGET_UNIX - if (g_UseRestrictedVirtualMemory) + // For 32-bit processes the virtual address range could be smaller than the amount of physical + // memory on the machine/in the container, we need to restrict by the VM. + if (ms.ullTotalVirtual < ms.ullTotalPhys) { - _ASSERTE (ms.ullTotalVirtual == restricted_limit); if (memory_load != NULL) *memory_load = (uint32_t)((float)(ms.ullTotalVirtual - ms.ullAvailVirtual) * 100.0 / (float)ms.ullTotalVirtual); if (available_physical != NULL)