The standard files/tools that report memory seem to have different formats on different Linux distributions. For example, on Arch and Ubuntu.

  • Arch

    $ free
                  total        used        free      shared  buff/cache   available
    Mem:        8169312     3870392     2648348       97884     1650572     4110336
    Swap:      16777212      389588    16387624
    
    
    $ head /proc/meminfo 
    MemTotal:        8169312 kB
    MemFree:         2625668 kB
    MemAvailable:    4088520 kB
    Buffers:          239688 kB
    Cached:          1224520 kB
    SwapCached:        17452 kB
    Active:          4074548 kB
    Inactive:        1035716 kB
    Active(anon):    3247948 kB
    Inactive(anon):   497684 kB
    
  • Ubuntu

    $ free
                 total       used       free     shared    buffers     cached
    Mem:      80642828   69076080   11566748    3063796     150688   58358264
    -/+ buffers/cache:   10567128   70075700
    Swap:     20971516    5828472   15143044
    
    
    $ head /proc/meminfo 
    MemTotal:       80642828 kB
    MemFree:        11565936 kB
    Buffers:          150688 kB
    Cached:         58358264 kB
    SwapCached:      2173912 kB
    Active:         27305364 kB
    Inactive:       40004480 kB
    Active(anon):    7584320 kB
    Inactive(anon):  4280400 kB
    Active(file):   19721044 kB
    

So, how can I portably (across Linux distros only) and reliably get the amount of memory—excluding swap—that is available for my software to use at a particular time? Presumably that's what's shown as "available" and "MemAvailable" in the output of free and cat /proc/meminfo in Arch but how do I get the same in Ubuntu or another distribution?

up vote 11 down vote accepted

MemAvailable is included in /proc/meminfo since version 3.14 of the kernel; it was added by commit 34e431b0a. That's the determining factor in the output variations you show. The commit message indicates how to estimate available memory without MemAvailable:

Currently, the amount of memory that is available for a new workload, without pushing the system into swap, can be estimated from MemFree, Active(file), Inactive(file), and SReclaimable, as well as the "low" watermarks from /proc/zoneinfo.

The low watermarks are the level beneath which the system will swap. So in the absence of MemAvailable you can at least add up the values given for MemFree, Active(file), Inactive(file) and SReclaimable (whichever are present in /proc/meminfo), and subtract the low watermarks from /proc/zoneinfo. The latter also lists the number of free pages per zone, that might be useful as a comparison...

The complete algorithm is given in the patch to meminfo.c and seems reasonably easy to adapt:

  • sum the low watermarks across all zones;
  • take the identified free memory (MemFree);
  • subtract the low watermark (we need to avoid touching that to avoid swapping);
  • add the amount of memory we can use from the page cache (sum of Active(file) and Inactive(file)): that's the amount of memory used by the page cache, minus either half the page cache, or the low watermark, whichever is smaller;
  • add the amount of memory we can reclaim (SReclaimable), following the same algorithm.

So, putting all this together, you can get the memory available for a new process with:

awk -v low=$(grep low /proc/zoneinfo | awk '{k+=$2}END{print k}') \
 '{a[$1]=$2}
  END{ 
   print a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]-(12*low); 
  }' /proc/meminfo 
  • Ah, good, so at least it should be portable across the same kernel version. That's something. I'm testing your suggestion with awk -v low=$(grep low /proc/zoneinfo | awk '{k+=$2}END{print k}') '{a[$1]=$2}END{m=a["MemFree:"]+a["Active(file):"]+a["Inactive(file):"]+a["SReclaimable:"]; print a["MemAvailable:"],m-low}' /proc/meminfo which should give me the same number printed twice. However, the second number (my understanding of the algorithm you suggest) is higher than the MemAvailable shown in /proc/meminfo. What am I doing wrong? – terdon Feb 10 '16 at 13:22
  • 1
    /proc/zoneinfo counts pages, which are mostly 4KB in size on amd64; you're also missing the extra safety added to the page cache and the reclaimable memory. Simplifying the latter, we can subtract the low watermark three times, so m-12*low (3 × 4KB) gives the correct result on my system. (This simplification underestimates available memory if the page cache or reclaimable memory is smaller than twice the low watermark, but you wouldn't want to use much memory in that situation anyway so that seems to be a reasonable compromise.) – Stephen Kitt Feb 10 '16 at 13:31
  • 1
    @StephenKitt how would you go about calculating it for older kernels that don't have either (file) entires or the SReclaimable entry? On an older centos box with kernel v 2.6.18-348.16.1.el5xen (per uname -r) this is the output I get: pastebin.com/iFWiM1kX . Your calculation only pulls the MemFree part – Mitch Nov 10 '16 at 19:07
  • @Mitch I don't know, I'm not sure the information available from your old kernel is sufficient to determine the available memory (before swapping) accurately. – Stephen Kitt Nov 10 '16 at 22:49

While Stephen's answer is perfectly sufficient and errs on the side of caution, I decided to write up the full logic including the minimum comparisons. Information is first read from /proc/meminfo and stored in a variable so that memory details are consistent.

LOW_WATERMARK=$(awk '$1 == "low" {LOW_WATERMARK += $2} END {print LOW_WATERMARK * 4096}' /proc/zoneinfo)

MEMINFO=$(</proc/meminfo)

MEMINFO_MEMFREE=$(echo "${MEMINFO}" | awk '$1 == "MemFree:" {print $2 * 1024}')
MEMINFO_FILE=$(echo "${MEMINFO}" | awk '{MEMINFO[$1]=$2} END {print (MEMINFO["Active(file):"] + MEMINFO["Inactive(file):"]) * 1024}')
MEMINFO_SRECLAIMABLE=$(echo "${MEMINFO}" | awk '$1 == "SReclaimable:" {print $2 * 1024}')

MEMINFO_MEMAVAILABLE=$((
  MEMINFO_MEMFREE - LOW_WATERMARK
  + MEMINFO_FILE - ((MEMINFO_FILE/2) < LOW_WATERMARK ? (MEMINFO_FILE/2) : LOW_WATERMARK)
  + MEMINFO_SRECLAIMABLE - ((MEMINFO_SRECLAIMABLE/2) < LOW_WATERMARK ? (MEMINFO_SRECLAIMABLE/2) : LOW_WATERMARK)
))

if [[ "${MEMINFO_MEMAVAILABLE}" -le 0 ]]
then
  MEMINFO_MEMAVAILABLE=0
fi

Result stored in variable is in bytes.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.