19

I would like my python script to use all the free RAM available but no more (for efficiency reasons). I can control this by reading in only a limited amount of data but I need to know how much RAM is free at run-time to get this right. It will be run on a variety of Linux systems. Is it possible to determine the free RAM at run-time?

oz123
  • 27,559
  • 27
  • 125
  • 187
marshall
  • 2,443
  • 7
  • 25
  • 45

5 Answers5

24

On Linux systems I use this from time to time:

def memory():
    """
    Get node total memory and memory usage
    """
    with open('/proc/meminfo', 'r') as mem:
        ret = {}
        tmp = 0
        for i in mem:
            sline = i.split()
            if str(sline[0]) == 'MemTotal:':
                ret['total'] = int(sline[1])
            elif str(sline[0]) in ('MemFree:', 'Buffers:', 'Cached:'):
                tmp += int(sline[1])
        ret['free'] = tmp
        ret['used'] = int(ret['total']) - int(ret['free'])
    return ret

You can run this when your script starts up. RAM is usually used and freed pretty frequently on a busy system, so you should take that into account before deciding how much RAM to use. Also, most linux systems have a swappiness value of 60. When using up memory, pages that are least frequently used will be swapped out. You may find yourself using SWAP instead of RAM.

Hope this helps.

Gabriel Samfira
  • 2,675
  • 1
  • 17
  • 19
  • What does a swappiness value of 60 mean? – marshall Jul 18 '13 at 08:47
  • @marshall http://askubuntu.com/questions/103915/how-do-i-configure-swappiness gives a fair account. – nic Jul 18 '13 at 08:52
  • Its the likelihood of the kernel to swap out pages. Values range from 0 to 100, where 0 is least likely to swap out (it usually does this to avoid an out of memory condition) and 100 is aggressive swapping. – Gabriel Samfira Jul 18 '13 at 08:52
10

Another option is the Python package psutil:

stats = psutil.virtual_memory()  # returns a named tuple
available = getattr(stats, 'available')
print(available)

According to the documentation, the available field "is calculated by summing different memory values depending on the platform and it is supposed to be used to monitor actual memory usage in a cross platform fashion."

Note the return value will be in bytes

Zulu
  • 8,765
  • 9
  • 49
  • 56
Addison Klinke
  • 1,024
  • 2
  • 14
  • 23
7

You could just read out /proc/meminfo. Be aware that the "free memory" is usually quite low, as the OS heavily uses free, unused memory for caching.

Also, it's best if you don't try to outsmart your OS's memory management. That usually just ends in tears (or slower programs). Better just take the RAM you need. If you want to use as much as you can on a machine with a previously unknown amount of memory, I'd probably check how much RAM is installed (MemTotal in /proc/meminfo), leave a certain amount for the OS and as safety margin (say 1 GB) and use the rest.

K3---rnc
  • 6,717
  • 3
  • 31
  • 46
Carsten
  • 17,991
  • 4
  • 48
  • 53
  • 1
    some *nix systems come without /proc though :( – nic Jul 18 '13 at 08:48
  • 2
    @nic `/proc/meminfo` with its current set of values [has been available since at least Linux 2.6](http://linux.die.net/man/5/proc). That should be long enough. – Carsten Jul 18 '13 at 08:55
  • 1
    There are other *nix systems than Linux, so for the purpose of generalisibility this deserves to be mentioned. – nic Jul 18 '13 at 08:57
  • The OP specified that it would be run on a variety of Linux systems. – Gabriel Samfira Jul 18 '13 at 08:59
  • 1
    True. Maybe we can agree on seeing my comment as odd curiosa for the interested reader then? :) – nic Jul 18 '13 at 09:01
  • Is the format of /proc/sysinfo completely fixed across linux varieties so it is easy to parse? – marshall Jul 18 '13 at 09:40
  • @marshall Yes, it is. Please see the [corresponding kernel source code](https://github.com/torvalds/linux/blob/8a72f3820c4d14b27ad5336aed00063a7a7f1bef/fs/proc/meminfo.c). – Carsten Jul 18 '13 at 10:05
  • @nic Yes. In the spirit of your comment, [procfs seems to be supported by](http://en.wikipedia.org/wiki/Procfs) at least Solaris, IRIX, Tru64, BSD (with varieties, including Mac OS X), AIX, QNX and Plan9. – Carsten Jul 18 '13 at 10:07
  • @Carsten BSD, excluding Mac OS X and soon excluding Free BSD i believe. But now it's semantics. – nic Jul 18 '13 at 10:31
  • @nic Sorry, my bad, you're right. I remembered OS X wrong. :) – Carsten Jul 18 '13 at 10:50
  • 1
    I thought you're *supposed* to outsmart the OS memory management because you know more about how you can break processing up into chunks and you know better what things are going to be used in the future. A numpy algorithm that relies on swap space is going to be much slower than one written in appropriately-sized chunks right? – endolith Jan 17 '15 at 17:06
  • @endolith Yes, you're right. Of course you'll be slower when using swap. So, if you can, and if you know how to break your program down into chunks that fit into memory, that's a good thing. I was just trying to caution the OP to not over-think this, i.e. trying to get your program to _"use all the free RAM available but no more_", because the free RAM might be much less than the amount that you could actually use because of all the file system cacheing going on. So just chunk your thing so that it fits into memory comfortably and you should be fine. – Carsten Jan 17 '15 at 19:24
  • Ah, OK. How do we determine how much RAM we can use for our chunks though? – endolith Jan 17 '15 at 19:26
  • @endolith As I've written in my answer, I'd probably get the total RAM, and chunk in such a way that it fits comfortably inside, leaving some space for the OS and a bit for safety. – Carsten Jan 17 '15 at 21:20
5

Since the original poster wrote the code should run on a variety of Linux system, I am posting here an Object oriented solution for Linux Memory query. psutil is a great library, but if you can't install it for some reason, you can simply use the following solution:

Example usage:

>>> f = FreeMemLinux()
>>> print f.total, f.used,  f.user_free
8029212 3765960 4464816
>>>
>>> f_mb = FreeMemLinux(unit='MB')
>>> print f_mb.total, f_mb.used,  f_mb.user_free
7841.02734375 3677.6953125 4360.171875
>>>
>>> f_percent = FreeMemLinux(unit='%')
>>> print f_percent.total, f_percent.used, f_percent.user_free
100.0 46.9032328453 55.60715049

Code:

class FreeMemLinux(object):
    """
    Non-cross platform way to get free memory on Linux. Note that this code
    uses the `with ... as`, which is conditionally Python 2.5 compatible!
    If for some reason you still have Python 2.5 on your system add in the
head of your code, before all imports:
    from __future__ import with_statement
    """

    def __init__(self, unit='kB'):

        with open('/proc/meminfo', 'r') as mem:
            lines = mem.readlines()

        self._tot = int(lines[0].split()[1])
        self._free = int(lines[1].split()[1])
        self._buff = int(lines[2].split()[1])
        self._cached = int(lines[3].split()[1])
        self._shared = int(lines[20].split()[1])
        self._swapt = int(lines[14].split()[1])
        self._swapf = int(lines[15].split()[1])
        self._swapu = self._swapt - self._swapf

        self.unit = unit
        self._convert = self._factor()

    def _factor(self):
        """determine the convertion factor"""
        if self.unit == 'kB':
            return 1
        if self.unit == 'k':
            return 1024.0
        if self.unit == 'MB':
            return 1/1024.0
        if self.unit == 'GB':
            return 1/1024.0/1024.0
        if self.unit == '%':
            return 1.0/self._tot
        else:
            raise Exception("Unit not understood")

    @property
    def total(self):
        return self._convert * self._tot

    @property
    def used(self):
        return self._convert * (self._tot - self._free)

    @property
    def used_real(self):
        """memory used which is not cache or buffers"""
        return self._convert * (self._tot - self._free -
                                self._buff - self._cached)

    @property
    def shared(self):
        return self._convert * (self._tot - self._free)

    @property
    def buffers(self):
        return self._convert * (self._buff)

    @property
    def cached(self):
        return self._convert * self._cached

    @property
    def user_free(self):
        """This is the free memory available for the user"""
        return self._convert *(self._free + self._buff + self._cached)

    @property
    def swap(self):
        return self._convert * self._swapt

    @property
    def swap_free(self):
        return self._convert * self._swapf

    @property
    def swap_used(self):
        return self._convert * self._swapu
oz123
  • 27,559
  • 27
  • 125
  • 187
  • Note that this isn't accurate, as the line positions of /proc/meminfo is different on my Ubuntu 18.04 system. E.g. line[1] is MemFree, but line[2] is MemAvailable on my system instead of Buffers (which is line[3] on my system). – Cardin Aug 07 '23 at 02:03
1

I suppose you could use free (http://www.cyberciti.biz/faq/linux-check-memory-usage/), ps or maybe the MemoryMonitor class from the SO-thread posted at the very bottom of my answer to do this. Just cut some slack and leave some small amount of ram unused for other processes if they should need it urgently yo avoid disk writes on their behalf.

You will need to parse the output from free or ps if you use that, but that shouldn't be hard. Remember that you need to analyze available ram real time, so you can adjust if another process gets memory hungry for some reason.

also see this thread: How to get current CPU and RAM usage in Python?

Community
  • 1
  • 1
nic
  • 443
  • 3
  • 12