The free utility is used to display the amount of free and used memory in the system. Debian Stretch provides an upgraded package. Output format has changed, and available memory is calculated using a different method, so let’s see what we can do with these modifications at this moment.

The old behavior

Used procps-ng version.

$ free -V
free from procps-ng 3.3.9

Standard output using the KiB unit.

$ free
total       used       free     shared    buffers     cached
Mem:      16358744   13444504    2914240     498912     189188    1973396
-/+ buffers/cache:   11281920    5076824
Swap:     15998972     252356   15746616

Standard output using the MiB unit.

$ free -m
total       used       free     shared    buffers     cached
Mem:         15975      13129       2845        487        184       1927
-/+ buffers/cache:      11017       4957
Swap:        15623        246      15377

The method used to calculate available memory depends directly on the proc filesystem: MemFree plus Buffers plus Cache fields read from /proc/meminfo file. It does not consider the amount of reclaimable memory used by the kernel to cache data structures, page cache, and reserved pages.

[...]
printf("%s\n", _("             total       used       free     shared    buffers     cached"));
printf("%-7s", _("Mem:"));
printf(" %10s", scale_size(kb_main_total, flags, args));
printf(" %10s", scale_size(kb_main_used, flags, args));
printf(" %10s", scale_size(kb_main_free, flags, args));
printf(" %10s", scale_size(kb_main_shared, flags, args));
printf(" %10s", scale_size(kb_main_buffers, flags, args));
printf(" %10s", scale_size(kb_main_cached, flags, args));
printf("\n");
[...]
if (!(flags & FREE_OLDFMT)) {
        unsigned KLONG buffers_plus_cached = kb_main_buffers + kb_main_cached;
        printf(_("-/+ buffers/cache:"));
        printf(" %10s",
                scale_size(kb_main_used - buffers_plus_cached, flags, args));
        printf(" %10s",
                scale_size(kb_main_free + buffers_plus_cached, flags, args));
        printf("\n");
}
[...]

The new behavior

Used procps-ng version.

$ free -V
free from procps-ng 3.3.10

Standard output using the KiB unit.

$ free
total        used        free      shared  buff/cache   available
Mem:       16358744     7324312     2914240      498912     6120192     4439720
Swap:      15998972      252356    15746616

Standard output using the MiB unit.

$ free -m
total        used        free      shared  buff/cache   available
Mem:          15975        7152        2845         487        5976        4335
Swap:         15623         246       15377

The method used to calculate available memory depends directly on the proc filesystem: MemAvailable field read from /proc/meminfo file.

[...]
if (flags & FREE_WIDE) {
        printf(_("              total        used        free      shared     buffers       cache   available"));
} else {
        printf(_("              total        used        free      shared  buff/cache   available"));
}
printf("\n");
printf("%-7s", _("Mem:"));
printf(" %11s", scale_size(kb_main_total, flags, args));
printf(" %11s", scale_size(kb_main_used, flags, args));
printf(" %11s", scale_size(kb_main_free, flags, args));
printf(" %11s", scale_size(kb_main_shared, flags, args));
if (flags & FREE_WIDE) {
        printf(" %11s", scale_size(kb_main_buffers, flags, args));
        printf(" %11s", scale_size(kb_main_cached, flags, args));
} else {
        printf(" %11s", scale_size(kb_main_buffers+kb_main_cached, flags, args));
}
printf(" %11s", scale_size(kb_main_available, flags, args));
printf("\n");
[...]

Excerpt from the free manual page.

available
       Estimation of how much memory is available  for  starting  new  applications,  without
       swapping.  Unlike the data provided by the cache or free fields, this field takes into
       account page cache and also that not all reclaimable memory slabs  will  be  reclaimed
       due  to  items being in use (MemAvailable in /proc/meminfo, available on kernels 3.14,
       emulated on kernels 2.6.27+, otherwise the same as free)

Excerpt from the /proc documentation.

MemAvailable: An estimate of how much memory is available for starting new
              applications, without swapping. Calculated from MemFree,
              SReclaimable, the size of the file LRU lists, and the low
              watermarks in each zone.
              The estimate takes into account that the system needs some
              page cache to function well, and that not all reclaimable
              slab will be reclaimable, due to items being in use. The
              impact of those factors will vary from system to system.

The function used to estimate available memory can be found in mm/page_alloc.c source file located in the kernel source tree.

long si_mem_available(void)
{
	long available;
	unsigned long pagecache;
	unsigned long wmark_low = 0;
	unsigned long pages[NR_LRU_LISTS];
	struct zone *zone;
	int lru;

	for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
		pages[lru] = global_node_page_state(NR_LRU_BASE + lru);

	for_each_zone(zone)
		wmark_low += zone->watermark[WMARK_LOW];

	/*
	 * Estimate the amount of memory available for userspace allocations,
	 * without causing swapping.
	 */
	available = global_zone_page_state(NR_FREE_PAGES) - totalreserve_pages;

	/*
	 * Not all the page cache can be freed, otherwise the system will
	 * start swapping. Assume at least half of the page cache, or the
	 * low watermark worth of cache, needs to stay.
	 */
	pagecache = pages[LRU_ACTIVE_FILE] + pages[LRU_INACTIVE_FILE];
	pagecache -= min(pagecache / 2, wmark_low);
	available += pagecache;

	/*
	 * Part of the reclaimable slab consists of items that are in use,
	 * and cannot be freed. Cap this estimate at the low watermark.
	 */
	available += global_node_page_state(NR_SLAB_RECLAIMABLE) -
		     min(global_node_page_state(NR_SLAB_RECLAIMABLE) / 2,
			 wmark_low);

	if (available < 0)
		available = 0;
	return available;
}
EXPORT_SYMBOL_GPL(si_mem_available);

Parse command output regardless of version

Use awk to parse free output and use different data depending on the content of the header line.

$ free -m | \
  awk '{
         if (NR==1) {
           if (/cached$/)
             {vesion=0}
           else
             {version=1}
         } else  
           if (/^Mem/) {
             if (version==0)
               {print "Free/Available: " $4 + $6 + $7 "/" $2 " MiB"}
             else
               {print "Free/Available: " $7 "/" $2 " MiB"}
           }
         }'

Output for the old free utility.

Free/Available: 4956/15975 MiB

Output for the new free utility.

Free/Available: 4335/15975 MiB

Parse proc filesystem

Inspect /proc/meminfo file.

$ cat /proc/meminfo
MemTotal:       16358744 kB
MemFree:         2914240 kB
MemAvailable:    4439720 kB
Buffers:          189188 kB
Cached:          1973396 kB
SwapCached:        86076 kB
Active:          2374260 kB
Inactive:         853088 kB
Active(anon):     894680 kB
Inactive(anon):   668992 kB
Active(file):    1479580 kB
Inactive(file):   184096 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:      15998972 kB
SwapFree:       15746616 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:        993164 kB
Mapped:           400748 kB
Shmem:            498912 kB
Slab:            3957608 kB
SReclaimable:     199856 kB
SUnreclaim:      3757752 kB
KernelStack:       21968 kB
PageTables:        33952 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:    24178344 kB
Committed_AS:    3655512 kB
VmallocTotal:   34359738367 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB
HardwareCorrupted:     0 kB
AnonHugePages:    751616 kB
CmaTotal:              0 kB
CmaFree:               0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:     7296640 kB
DirectMap2M:     9406464 kB

Parse this file to read and display available/total memory.

$ awk '{
    if (/MemAvailable:/) {mem_available=$2};
    if (/MemTotal:/) {mem_total=$2};
    if (mem_available && mem_total)
      {print "Available/Total memory: " int(mem_available/1024) "/" int(mem_total/1024) " MiB";exit}
  }' /proc/meminfo
Available/Total memory: 4335/15975 MiB
ko-fi