How can I get the amount of available memory portably across distributions?
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)
, andSReclaimable
, 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)
andInactive(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
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.