C/C++ tip: How to get the physical memory size of a system

Topics: C/C++

API functions to get the size of physical memory (RAM) differ between Windows, Linux, OSX, AIX, BSD, Solaris, and other UNIX-style OSes. This article provides a cross-platform function to get the physical memory size, and explains what works on what OS.

How to get physical memory size

Physical memory is the RAM installed on a system. The size of physical memory is a hard upper bound on the size of a process before parts of it have to be paged to disk or SSD. In practice, some of physical memory is permanently allocated to the kernel and other services that have to stay resident in memory. The remainder of physical memory is managed by the OS and shared among all running processes. No one process will ever get all of physical memory.

Each OS has command-line or user interface tools to report the physical memory size, but checking it programmatically is useful for large high-performance code that automatically adjusts memory use to different system sizes. Unfortunately, API functions to get the memory size are not standardized and differ from OS to OS.

Code

The following getMemorySize( ) function works for most OSes (copy and paste, or download getMemorySize.c). Linking with the default libraries is sufficient.

See the sections that follow for discussion, caveats, and why this code requires so many #ifdef's.

/*
 * Author:  David Robert Nadeau
 * Site:    http://NadeauSoftware.com/
 * License: Creative Commons Attribution 3.0 Unported License
 *          http://creativecommons.org/licenses/by/3.0/deed.en_US
 */

#if defined(_WIN32)
#include <Windows.h>

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#if defined(BSD)
#include <sys/sysctl.h>
#endif

#else
#error "Unable to define getMemorySize( ) for an unknown OS."
#endif



/**
 * Returns the size of physical memory (RAM) in bytes.
 */
size_t getMemorySize( )
{
#if defined(_WIN32) && (defined(__CYGWIN__) || defined(__CYGWIN32__))
	/* Cygwin under Windows. ------------------------------------ */
	/* New 64-bit MEMORYSTATUSEX isn't available.  Use old 32.bit */
	MEMORYSTATUS status;
	status.dwLength = sizeof(status);
	GlobalMemoryStatus( &status );
	return (size_t)status.dwTotalPhys;

#elif defined(_WIN32)
	/* Windows. ------------------------------------------------- */
	/* Use new 64-bit MEMORYSTATUSEX, not old 32-bit MEMORYSTATUS */
	MEMORYSTATUSEX status;
	status.dwLength = sizeof(status);
	GlobalMemoryStatusEx( &status );
	return (size_t)status.ullTotalPhys;

#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
	/* UNIX variants. ------------------------------------------- */
	/* Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM */

#if defined(CTL_HW) && (defined(HW_MEMSIZE) || defined(HW_PHYSMEM64))
	int mib[2];
	mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
	mib[1] = HW_MEMSIZE;            /* OSX. --------------------- */
#elif defined(HW_PHYSMEM64)
	mib[1] = HW_PHYSMEM64;          /* NetBSD, OpenBSD. --------- */
#endif
	int64_t size = 0;               /* 64-bit */
	size_t len = sizeof( size );
	if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
		return (size_t)size;
	return 0L;			/* Failed? */

#elif defined(_SC_AIX_REALMEM)
	/* AIX. ----------------------------------------------------- */
	return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L;

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
	/* FreeBSD, Linux, OpenBSD, and Solaris. -------------------- */
	return (size_t)sysconf( _SC_PHYS_PAGES ) *
		(size_t)sysconf( _SC_PAGESIZE );

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
	/* Legacy. -------------------------------------------------- */
	return (size_t)sysconf( _SC_PHYS_PAGES ) *
		(size_t)sysconf( _SC_PAGE_SIZE );

#elif defined(CTL_HW) && (defined(HW_PHYSMEM) || defined(HW_REALMEM))
	/* DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX. -------- */
	int mib[2];
	mib[0] = CTL_HW;
#if defined(HW_REALMEM)
	mib[1] = HW_REALMEM;		/* FreeBSD. ----------------- */
#elif defined(HW_PYSMEM)
	mib[1] = HW_PHYSMEM;		/* Others. ------------------ */
#endif
	unsigned int size = 0;		/* 32-bit */
	size_t len = sizeof( size );
	if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
		return (size_t)size;
	return 0L;			/* Failed? */
#endif /* sysctl and sysconf variants */

#else
	return 0L;			/* Unknown OS. */
#endif
}

Usage

Just call the function to get the memory size in bytes. If the returned value is zero, the call failed due to OS limitations.

size_t memorySize = getMemorySize( );

Discussion

Each OS has one or more ways of getting the physical memory size:

OS CPU time
AIX sysconf( ) with _SC_AIX_REALMEM
Cygwin GlobalMemoryStatus( )
DragonFly BSD sysctl( ) with HW_PHYSMEM
FreeBSD sysconf( ) with _SC_PHYS_PAGES, or sysctl( ) with HW_REALMEM or HW_PHYSMEM
Linux sysconf( ) with _SC_PHYS_PAGES, sysinfo( ), or /proc/meminfo
NetBSD sysctl( ) with HW_PHYSMEM64 or HW_PHYSMEM
OpenBSD sysconf( ) with _SC_PHYS_PAGES, or sysctl( ) with HW_PHYSMEM64 or HW_PHYSMEM
OSX sysconf( ) with _SC_PHYS_PAGES, or sysctl( ) with HW_MEMSIZE or HW_PHYSMEM
Solaris sysconf( ) with _SC_PHYS_PAGES
Windows GlobalMemoryStatus( ) or GlobalMemoryStatusEx( )

Each of these is discussed below.

GlobalMemoryStatus( ) and GlobalMemoryStatusEx( )

On Windows and Cygwin (Linux compatibility for Windows), the GlobalMemoryStatus( ) function fills a MEMORYSTATUS struct with information about system memory. Structure fields include:

typedef struct _MEMORYSTATUS {
	DWORD  dwLength;
	DWORD  dwMemoryLoad;
	SIZE_T dwTotalPhys;
	SIZE_T dwAvailPhys;
	SIZE_T dwTotalPageFile;
	SIZE_T dwAvailPageFile;
	SIZE_T dwTotalVirtual;
	SIZE_T dwAvailVirtual;
} MEMORYSTATUS, *LPMEMORYSTATUS;

The dwTotalPhys field contains the physical memory size in bytes. However, the field is only large enough to hold a 32-bit integer. For systems with more than 4 Gbytes of memory, the field is set to -1.

On Windows, but not Cygwin, the new GlobalMemoryStatusEx( ) function fills a 64-bit safe MEMORYSTATUSEX struct with information about physical and virtual memory. Structure fields include:

typedef struct _MEMORYSTATUSEX {
	DWORD     dwLength;
	DWORD     dwMemoryLoad;
	DWORDLONG ullTotalPhys;
	DWORDLONG ullAvailPhys;
	DWORDLONG ullTotalPageFile;
	DWORDLONG ullAvailPageFile;
	DWORDLONG ullTotalVirtual;
	DWORDLONG ullAvailVirtual;
	DWORDLONG ullAvailExtendedVirtual;
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

The 64-bit ullTotalPhys field contains the physical memory size in bytes.

Beware: The GetPhysicallyInstalledSystemMemory( ) function (from Vista onwards) returns the size of all memory installed on the system. This value comes from the BIOS and may be larger than the value reported by GlobalMemoryStatusEx( ) if the BIOS and low level drivers reserve some memory for memory-mapped devices. While the value from GetPhysicallyInstalledSystemMemory( ) may be more literally correct, it's less useful. The amount of memory actually available for use by the OS and processes is usually what we want.

Availability: Cygwin and Windows XP and later.

Get memory size:

#include <Windows.h>
...

#if defined(__CYGWIN__) || defined(__CYGWIN32__)
	MEMORYSTATUS status;
	status.dwLength = sizeof(status);
	GlobalMemoryStatus( &status );
	return (size_t)status.dwTotalPhys;
#else
	MEMORYSTATUSEX status;
	status.dwLength = sizeof(status);
	GlobalMemoryStatusEx( &status );
	return (size_t)status.ullTotalPhys;
#endif

sysconf( )

On AIX, FreeBSD, Linux, OpenBSD, and Solaris, sysconf( ) returns basic system configuration information (see man pages for AIX, FreeBSD, Linux, OpenBSD, OSX, and Solaris). For FreeBSD and OpenBSD, the function is available but sysctl( ) provides more information and a more accurate measure of physical memory (see below).

The sysconf( ) function takes an attribute argument and returns a long integer giving the attribute's value. Though sysconf( ) is defined by POSIX, the attribute for physical memory size is not standardized and varies among the OSes:

OS sysconf( ) attribute Value
AIX _SC_AIX_REALMEM Physical memory size in kilobytes
FreeBSD, Linux, OpenBSD, Solaris _SC_PHYS_PAGES Physical memory size in pages

For _SC_AIX_REALMEM, the return value is in kilobytes.

For _SC_PHYS_PAGES, the return value is in pages. To convert to bytes, multiply it by the page size from sysconf( ) using the _SC_PAGESIZE (or legacy _SC_PAGE_SIZE) attribute. FreeBSD, Linux, and OpenBSD, also provide a getPageSize( ) function that returns the same value. The physical memory size calculated using the page size is rounded down to the nearest page boundary and may be a bit less than the actual memory on the system.

Availability: AIX, FreeBSD, Linux, OpenBSD, and Solaris.

Get memory size:

#include <sys/sysctl.h>
#include <sys/types.h>
...

#if defined(_SC_AIX_REALMEM)
	return (size_t)sysconf( _SC_AIX_REALMEM ) * (size_t)1024L;

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGESIZE)
	return (size_t)sysconf( _SC_PHYS_PAGES ) * (size_t)sysconf( _SC_PAGESIZE );

#elif defined(_SC_PHYS_PAGES) && defined(_SC_PAGE_SIZE)
	return (size_t)sysconf( _SC_PHYS_PAGES ) * (size_t)sysconf( _SC_PAGE_SIZE );
#endif

sysctl( )

On BSD and OSX, sysctl( ) is the preferred method to get a wide variety of system configuration information (see man pages for DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX). Linux also provides the function, but it's deprecated and strongly discouraged in favor of sysconf( ) (see above).

The sysctl( ) function takes an array of integers that select a configuration attribute to query. On success, the function fills a variable with the attribute's value. Attributes are grouped hierarchically and include information about system hardware, the kernel, the network, virtual memory, and more. Hardware attributes are grouped under CTL_HW. The hardware attribute for physical memory size varies among the OSes:

OS CTL_HW attribute Value
FreeBSD HW_REALMEM Physical memory size in bytes (32-bit integer)
DragonFly BSD, FreeBSD, NetBSD, OpenBSD, OSX HW_PHYSMEM Physical memory size in bytes (32-bit integer)
NetBSD, OpenBSD HW_PHYSMEM64 Physical memory size in bytes (64-bit integer)
OSX HW_MEMSIZE Physical memory size in bytes (64-bit integer)

The older HW_PHYSMEM attribute is available for BSD and OSX, but it returns a 32-bit unsigned integer that's too small to hold memory sizes larger than 2 Gbytes. For memory size as a 64-bit integer, use HW_PHYSMEM64 on NetBSD and OpenBSD, and HW_MEMSIZE on OSX. Unfortunately, FreeBSD and DragonFly BSD currently do not support an attribute to get the memory size in 64-bits.

FreeBSD and OpenBSD provide both sysctl( ) and sysconf( ) (see above) to get the physical memory size. The sysctl( ) method is more accurate since it returns the size in bytes, while sysconf( ) returns the size rounded down to the nearest page boundary.

FreeBSD provides both HW_REALMEM and HW_PHYSMEM. Both return 32-bit integers, but HW_PHYSMEM's value is rounded down to the nearest page boundary, like the value computed from sysconf( ), while HW_REALMEM's value is not. Between, HW_PHYSMEM and sysconf( ), the latter is the better choice. While sysconf( ) returns a 32-bit integer too, it's in units of the page size, which is usually 4 Kbytes. This adds another 12 bits to the maximum memory size that can be reported (which is 16 Tbytes).

Availability: DragonFly BSD, FreeBSD, NetBSD, OpenBSD, and OSX.

Get memory size:

#include <unistd.h>
...

	int mib[2];
	mib[0] = CTL_HW;
#if defined(HW_MEMSIZE)
	mib[1] = HW_MEMSIZE;		/* OSX. --------------------- */
	int64_t size = 0;		/* 64-bit */
#elif defined(HW_PHYSMEM64)
	mib[1] = HW_PHYSMEM64;		/* NetBSD, OpenBSD. --------- */
	int64_t size = 0;		/* 64-bit */
#elif defined(HW_REALMEM)
	mib[1] = HW_REALMEM;		/* FreeBSD. ----------------- */
	unsigned int size = 0;		/* 32-bit */
#elif defined(HW_PHYSMEM)
	mib[1] = HW_PHYSMEM;		/* DragonFly BSD. ----------- */
	unsigned int size = 0;		/* 32-bit */
#endif
	size_t len = sizeof( size );
	if ( sysctl( mib, 2, &size, &len, NULL, 0 ) == 0 )
		return (size_t)size;
	return 0L;	

sysinfo( )

On Linux, the sysinfo( ) function fills a sysinfo struct with system statistics. The struct has the following fields:

struct sysinfo {
	long uptime;   		 /* Seconds since boot */
	unsigned long loads[3];  /* 1, 5, and 15 minute load averages */
	unsigned long totalram;  /* Total usable main memory size */
	unsigned long freeram;   /* Available memory size */
	unsigned long sharedram; /* Amount of shared memory */
	unsigned long bufferram; /* Memory used by buffers */
	unsigned long totalswap; /* Total swap space size */
	unsigned long freeswap;  /* swap space still available */
	unsigned short procs;    /* Number of current processes */
	unsigned long totalhigh; /* Total high memory size */
	unsigned long freehigh;  /* Available high memory size */
	unsigned int mem_unit;   /* Memory unit size in bytes */
	char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding for libc5 */
};

Prior to Linux 2.3.23 (late 2003), the sysinfo struct omitted the last four fields and all sizes were in bytes. Today, the larger struct is used and all sizes are in units given by the mem_unit field. The totalram field times the mem_unit field gives the size of physical memory in bytes.

The memory size computed from the sysinfo struct is the same as that computed from sysconf( ) and the page size (see above). Since the method is redundant, this article's getMemorySize( ) function uses sysconf( ) instead of sysinfo( ).

Availability: Linux.

Get memory size:

#include <sys/sysinfo.h>
...

struct sysinfo info;
sysinfo( &info );
return (size_t)info.totalram * (size_t)info.mem_unit;

/proc/meminfo

On Linux, the /proc pseudo-file system includes several pseudo-files filled with system configuration information. /proc/meminfo contains detailed information about memory use. Here's sample output from 64-bit Ubuntu Linux 12:

MemTotal:        3016120 kB
MemFree:         2188204 kB
Buffers:           25852 kB
Cached:           322260 kB
SwapCached:            0 kB
Active:           420580 kB
Inactive:         293120 kB
Active(anon):     366248 kB
Inactive(anon):    16356 kB
Active(file):      54332 kB
Inactive(file):   276764 kB
Unevictable:          12 kB
Mlocked:              12 kB
SwapTotal:       1046524 kB
SwapFree:        1046524 kB
Dirty:                16 kB
Writeback:             0 kB
AnonPages:        365632 kB
Mapped:            94504 kB
Shmem:             17016 kB
Slab:              37596 kB
SReclaimable:      19352 kB
SUnreclaim:        18244 kB
KernelStack:        2552 kB
PageTables:        22428 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     2554584 kB
Committed_AS:    2037044 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       21548 kB
VmallocChunk:   34359713720 kB
HardwareCorrupted:     0 kB
AnonHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       38848 kB
DirectMap2M:     3106816 kB

The format of the file varies a little from Linux to Linux, but all of them have a MemTotal line giving the usable physical memory size in kilobytes. This value may be a bit less than actual physical memory since it excludes memory used by the kernel.

Parsing /proc/meminfo is slower and more involved than calling sysinfo( ) or sysconf( ) (see above). This article's getMemorySize( ) function therefore uses the simpler sysconf( ).

Availability: Linux.

Get memory size:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
...

	FILE* fp = fopen( "/proc/meminfo", "r" );
	if ( fp != NULL )
	{
		size_t bufsize = 1024 * sizeof(char);
		char* buf      = (char*)malloc( bufsize );
		long value     = -1L;
		while ( getline( &buf, &bufsize, fp ) >= 0 )
		{
			if ( strncmp( buf, "MemTotal", 8 ) != 0 )
				continue;
			sscanf( buf, "%*s%ld", amp;&value );
			break;
		}
		fclose( fp );
		free( (void*)buf );
		if ( value != -1L )
			return (size_t)value * 1024L );
	}

Other

Every OS with a windowing interface has a control panel somewhere that shows the installed memory size. On Windows, see the "System" control panel. On OSX, select "About This Mac" from the Apple menu. On Solaris, select "About Oracle Solaris" from the "System" menu. For Ubuntu Linux, select the "Details" control panel. And so forth.

Every OS with a command line has commands to show the system configuration. On Windows, see the systeminfo command. On Linux, see free and vmstat. On BSD and OSX, use sysctl. On OSX, use system_profiler. On Solaris, use prtconf. And so on.

Downloads

Further reading

Related articles at NadeauSoftware.com

Web articles

Comments

Very helpful info!

Very helpful info! Thanks!

But is sysctl() HW_REALMEM on FreeBSD really "Physical memory size in bytes (64-bit integer)"? Seems to me it is "unsigned long" (4 bytes on 32-bit FreeBSD, 8 bytes on 64-bit FreeBSD).

Re: Very helpful info!

Thanks for catching a bug. The proper return type for FreeBSD's HW_REALMEM is a 32-bit integer, not the 64-bit integer used in the original version of this article.

Code for HP-UX

Following code will make the program work on HP-UX as well.
/* HP-UX. --------------------------------------------------- */
struct pst_static pst;

if (pstat_getstatic(&pst, sizeof(pst), (size_t) 1, 0) != -1)
	return (size_t)pst.physical_memory * (size_t)pst.page_size;
return 0L;

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • Web page addresses and e-mail addresses turn into links automatically.

More information about formatting options

Nadeau software consulting
Nadeau software consulting