C - Check currently available free RAM?

24,445

Solution 1

No, there's no standard C function to do that. There are some platform-specific functions you can use to perform certain types of queries (like working set size), but those probably won't be helpful, because sometimes memory which has been properly free()d is still considered to be allocated by the OS because the malloc implementation might keep the freed memory around in a pool.

If you want to check for memory leaks, I highly recommend using a tool like Valgrind, which runs your program in a virtual machine of sorts and can track memory leaks, among other features.

If you're running on Windows, you can use _CrtDbgReport and/or _CrtSetDbgFlag to check for memory leaks.

Solution 2

Linux glibc sysconf(_SC_AVPHYS_PAGES) and get_avphys_pages()

These two glibc extensions should give you the available number of pages. We can then just multiply that by the pages size sysconf(_SC_PAGESIZE) to find the total available memory in bytes.

main.c

#define _GNU_SOURCE
#include <stdio.h>
#include <sys/sysinfo.h>
#include <unistd.h>

int main(void) {
    /* PAGESIZE is POSIX: http://pubs.opengroup.org/onlinepubs/9699919799/
     * but PHYS_PAGES and AVPHYS_PAGES are glibc extensions. I bet those are
     * parsed from /proc/meminfo. */
    printf(
        "sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x%lX\n",
        sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)
    );
    printf(
        "sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x%lX\n",
        sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE)
    );

    /* glibc extensions. man says they are parsed from /proc/meminfo. */
    printf(
        "get_phys_pages() * sysconf(_SC_PAGESIZE) = 0x%lX\n",
        get_phys_pages() * sysconf(_SC_PAGESIZE)
    );
    printf(
        "get_avphys_pages() * sysconf(_SC_PAGESIZE) = 0x%lX\n",
        get_avphys_pages() * sysconf(_SC_PAGESIZE)
    );
}

GitHub upstream.

Compile and run:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

sample output on my 32GiB RAM system:

sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x7CCFFC000
sysconf(_SC_AVPHYS_PAGES) * sysconf(_SC_PAGESIZE) = 0x6383FD000
get_phys_pages() * sysconf(_SC_PAGESIZE) = 0x7CCFFC000
get_avphys_pages() * sysconf(_SC_PAGESIZE) = 0x6383FD000

0x7CCFFC000 is a bit smaller than 32GiB, and is the total RAM. 0x6383FD000 is the available one.

man get_avphys_pages says it gets its data from /proc/meminfo.

Tested in Ubuntu 19.04.

Solution 3

If in your system malloc() always allocates physical memory, you can call malloc() repeatedly with sizes differing not by 1, but by successive powers of two. That'll be more efficient. Below is an example of how to do it.

If, on the other hand, malloc() only allocates virtual address space without mapping physical memory into it, this won't give you what you want.

Sample code:

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

void* AllocateLargestFreeBlock(size_t* Size)
{
  size_t s0, s1;
  void* p;

  s0 = ~(size_t)0 ^ (~(size_t)0 >> 1);

  while (s0 && (p = malloc(s0)) == NULL)
    s0 >>= 1;

  if (p)
    free(p);

  s1 = s0 >> 1;

  while (s1)
  {
    if ((p = malloc(s0 + s1)) != NULL)
    {
      s0 += s1;
      free(p);
    }
    s1 >>= 1;
  }

  while (s0 && (p = malloc(s0)) == NULL)
    s0 ^= s0 & -s0;

  *Size = s0;
  return p;
}

size_t GetFreeSize(void)
{
  size_t total = 0;
  void* pFirst = NULL;
  void* pLast = NULL;

  for (;;)
  {
    size_t largest;
    void* p = AllocateLargestFreeBlock(&largest);

    if (largest < sizeof(void*))
    {
      if (p != NULL)
        free(p);
      break;
    }

    *(void**)p = NULL;

    total += largest;

    if (pFirst == NULL)
      pFirst = p;

    if (pLast != NULL)
      *(void**)pLast = p;

    pLast = p;
  }

  while (pFirst != NULL)
  {
    void* p = *(void**)pFirst;
    free(pFirst);
    pFirst = p;
  }

  return total;
}

int main(void)
{
  printf("Total free: %zu\n", GetFreeSize());
  printf("Total free: %zu\n", GetFreeSize());
  printf("Total free: %zu\n", GetFreeSize());
  printf("Total free: %zu\n", GetFreeSize());
  printf("Total free: %zu\n", GetFreeSize());
  return 0;
}

Output (ideone):

Total free: 266677120
Total free: 266673024
Total free: 266673024
Total free: 266673024
Total free: 266673024

Solution 4

If you can afford #ifdef'ing a debug version (possibly in an emulator!), you could just build a debug version of malloc/free that keeps track of the number of bytes currently in use, and "print" it periodically (again - only in the debug version, possibly under an emulator) on whatever output device you have for debugging (a led?), and see if it keeps increasing.

The standard trick is to allocate sizeof(size_t) more than requested, thus storing the size together with the newly allocated memory - but if you're writing a firmware I guess you know it already :)

So... do you have an emulator?

EDIT: I'm so used to computers running at GHz that it didn't occur to me at first, but of course another thing you can do is to just count the number of allocations, not their size -- I can't imagine how this could take too much memory to run.

Share:
24,445
Maestro
Author by

Maestro

Updated on March 31, 2022

Comments

  • Maestro
    Maestro about 2 years

    I know how to use malloc() and free() to allocate memory, but is there also a standard C function to check how much memory is left, so I can call that periodically to make sure my code has no memory leaks?

    The only thing I can think of is calling malloc(1) in a endless loop until it returns an error, but shouldn't there be a more efficient way?

    • Mike
      Mike over 11 years
      Why not just use valgrind on your program to check for leaks?
    • Ben
      Ben over 11 years
      note that calling malloc in an endless loop is likely to never fail since most systems only allocate memory on first touch.
    • Faruk Sahin
      Faruk Sahin over 11 years
    • Fred Foo
      Fred Foo over 11 years
      @Ben: every malloc call reserves part of the virtual address space, so it will eventually return an error.
    • Maestro
      Maestro over 11 years
      @Mike See my comment to Adam
    • Ben
      Ben over 11 years
      @larsmans, True, in fact malloc also needs (physical) memory to keep track of the allocated blocs (even if you drop the pointers). So you cannot malloc forever. However, this doesn't help at all to know how much memory is left.
    • AlphaGoku
      AlphaGoku over 6 years
      You could find the address in Ram before you want an overflow to be detected.Write a specific value to it and keep watching it in main() to see if it is corrupted
    • fdk1342
      fdk1342 over 5 years
      There is no standard way. But your OS / compiler / c library may have some additional functions that may give you the information you want. Posting that information would be useful.
  • Maestro
    Maestro over 11 years
    The program is firmware running on a Cortex M0 CPU, and uses a lot of ARM specific calls/instructions, so I think it would be hard to analyze for Valgrind, and I have barely enough memory on the device to run my own code, let alone to add a virtual machine.
  • Alex Reynolds
    Alex Reynolds over 11 years
    In that case, measure how much memory you have at the start of execution and write wrapper functions around malloc and free to decrement and increment from your start point, as loreb suggests in another answer.
  • AlfredD
    AlfredD over 5 years
    That doesn't take into account the extra bytes allocated by certain implementations for every malloc'ed buffer (for alignment purposes, or validating buffer overruns)
  • Alexey Frunze
    Alexey Frunze over 5 years
    @AlfredD malloc() wouldn't let you have those bytes anyway.