Skip to content

Commit

Permalink
Fix computing available memory on OSX for GC (#97102)
Browse files Browse the repository at this point in the history
* Fix computing available memory on OSX for GC

We were using the `vm_statistics_data_t::free_count` as the available memory
reported to the GC. It turned out this value is only a small portion of the
available memory and that the appropriate value should be based on the
kern.memorystatus_level value obtained using sysctl. That value represents percentual
amount of available memory, so multiplying it by the total memory bytes gets
the available memory bytes.

Close #94846

* Reflect PR feeback and move total memory computation to init
  • Loading branch information
janvorli authored Jan 25, 2024
1 parent 4e997b6 commit ffc1174
Showing 1 changed file with 65 additions and 41 deletions.
106 changes: 65 additions & 41 deletions src/coreclr/gc/unix/gcenv.unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ AffinitySet g_processAffinitySet;
extern "C" int g_highestNumaNode;
extern "C" bool g_numaAvailable;

static int64_t g_totalPhysicalMemSize = 0;

#ifdef TARGET_APPLE
static int *g_kern_memorystatus_level_mib = NULL;
static size_t g_kern_memorystatus_level_mib_length = 0;
#endif

// Initialize the interface implementation
// Return:
// true if it has succeeded, false if it has failed
Expand Down Expand Up @@ -317,6 +324,55 @@ bool GCToOSInterface::Initialize()

NUMASupportInitialize();

#ifdef TARGET_APPLE
const char* mem_free_name = "kern.memorystatus_level";
int rc = sysctlnametomib(mem_free_name, NULL, &g_kern_memorystatus_level_mib_length);
if (rc != 0)
{
return false;
}

g_kern_memorystatus_level_mib = (int*)malloc(g_kern_memorystatus_level_mib_length * sizeof(int));
if (g_kern_memorystatus_level_mib == NULL)
{
return false;
}

rc = sysctlnametomib(mem_free_name, g_kern_memorystatus_level_mib, &g_kern_memorystatus_level_mib_length);
if (rc != 0)
{
free(g_kern_memorystatus_level_mib);
g_kern_memorystatus_level_mib = NULL;
g_kern_memorystatus_level_mib_length = 0;
return false;
}
#endif

// Get the physical memory size
#if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
long pages = sysconf(_SC_PHYS_PAGES);
if (pages == -1)
{
return false;
}

g_totalPhysicalMemSize = (uint64_t)pages * (uint64_t)g_pageSizeUnixInl;
#elif HAVE_SYSCTL
int mib[2];
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
size_t length = sizeof(INT64);
int rc = sysctl(mib, 2, &g_totalPhysicalMemSize, &length, NULL, 0);
if (rc == 0)
{
return false;
}
#else // HAVE_SYSCTL
#error "Don't know how to get total physical memory on this platform"
#endif // HAVE_SYSCTL

assert(g_totalPhysicalMemSize != 0);

return true;
}

Expand Down Expand Up @@ -1211,34 +1267,7 @@ uint64_t GCToOSInterface::GetPhysicalMemoryLimit(bool* is_restricted)
return restricted_limit;
}

// Get the physical memory size
#if HAVE_SYSCONF && HAVE__SC_PHYS_PAGES
long pages = sysconf(_SC_PHYS_PAGES);
if (pages == -1)
{
return 0;
}

long pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize == -1)
{
return 0;
}

return (uint64_t)pages * (uint64_t)pageSize;
#elif HAVE_SYSCTL
int mib[3];
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
int64_t physical_memory = 0;
size_t length = sizeof(INT64);
int rc = sysctl(mib, 2, &physical_memory, &length, NULL, 0);
assert(rc == 0);

return physical_memory;
#else // HAVE_SYSCTL
#error "Don't know how to get total physical memory on this platform"
#endif // HAVE_SYSCTL
return g_totalPhysicalMemSize;
}

// Get amount of physical memory available for use in the system
Expand All @@ -1248,20 +1277,15 @@ uint64_t GetAvailablePhysicalMemory()

// Get the physical memory available.
#if defined(__APPLE__)
vm_size_t page_size;
mach_port_t mach_port;
mach_msg_type_number_t count;
vm_statistics_data_t vm_stats;
mach_port = mach_host_self();
count = sizeof(vm_stats) / sizeof(natural_t);
if (KERN_SUCCESS == host_page_size(mach_port, &page_size))
uint32_t mem_free = 0;
size_t mem_free_length = sizeof(uint32_t);
assert(g_kern_memorystatus_level_mib != NULL);
int rc = sysctl(g_kern_memorystatus_level_mib, g_kern_memorystatus_level_mib_length, &mem_free, &mem_free_length, NULL, 0);
assert(rc == 0);
if (rc == 0)
{
if (KERN_SUCCESS == host_statistics(mach_port, HOST_VM_INFO, (host_info_t)&vm_stats, &count))
{
available = (int64_t)vm_stats.free_count * (int64_t)page_size;
}
available = (int64_t)mem_free * g_totalPhysicalMemSize / 100;
}
mach_port_deallocate(mach_task_self(), mach_port);
#elif defined(__FreeBSD__)
size_t inactive_count = 0, laundry_count = 0, free_count = 0;
size_t sz = sizeof(inactive_count);
Expand All @@ -1279,7 +1303,7 @@ uint64_t GetAvailablePhysicalMemory()

if (tryReadMemInfo)
{
// Ensure that we don't try to read the /proc/meminfo in successive calls to the GlobalMemoryStatusEx
// Ensure that we don't try to read the /proc/meminfo in successive calls to the GetAvailablePhysicalMemory
// if we have failed to access the file or the file didn't contain the MemAvailable value.
tryReadMemInfo = ReadMemAvailable(&available);
}
Expand Down

0 comments on commit ffc1174

Please sign in to comment.