21

This is a follow-up to Memory limiting solutions for greedy applications that can crash OS?: ulimit and cgroups are not user friendly, and besides, wouldn't work with applications that spawn separate processes, such as Chrome/Chromium for each new (group of) tabs.

The simple and effective solution, used by Windows 7 actually, is to warn the user that the OS is running low on memory. This simple warning pop-up has prevented me from having any low-memory-caused system freeze in Windows, while I kept running into them on Ubuntu distros that I was testing live (where the RAM-mounted disk would eat up 2GB alone).

So, is there some way to automatically warn the user that the available RAM is nearing zero, without the user having to keep an eye on some memory monitoring gadget? Surely Conky could be configured to do that?

7 Answers7

12

Check these scripts: Need application/script alerting when system memory is running out

#!/bin/bash

#Minimum available memory limit, MB
THRESHOLD=400

#Check time interval, sec
INTERVAL=30

while :
do

    free=$(free -m|awk '/^Mem:/{print $4}')
    buffers=$(free -m|awk '/^Mem:/{print $6}')
    cached=$(free -m|awk '/^Mem:/{print $7}')
    available=$(free -m | awk '/^-\/+/{print $4}')

    message="Free $free""MB"", buffers $buffers""MB"", cached $cached""MB"", available $available""MB"""

    if [ $available -lt $THRESHOLD ]
        then
        notify-send "Memory is running out!" "$message"
    fi

    echo $message

    sleep $INTERVAL

done

PHP:

#!/usr/bin/php
<?php
$alert_percent=($argc>1)?(int)$argv[1]:90;
//$interval=($argc>2):(int)$argv[2]:25;



//while(true)
//{
 exec("free",$free);

$free=implode(' ',$free);
preg_match_all("/(?<=\s)\d+/",$free,$match);

list($total_mem,$used_mem,$free_mem,$shared_mem,$buffered_mem,$cached_mem)=$match[0];

$used_mem-=($buffered_mem+$cached_mem);

$percent_used=(int)(($used_mem*100)/$total_mem);

if($percent_used>$alert_percent)
exec("notify-send 'Low Memory: $percent_used% used'");

//sleep($interval);
//}
exit();
?>
  • 1
    The script works with small adaptations (I just used available=$(free -m | grep Mem | awk '{print $7}')). To make notify-send work with cron, refer to https://anmolsinghjaggi.wordpress.com/2016/05/11/notify-send-in-ubuntu-16-lts/ – morsch Aug 15 '16 at 21:50
  • If the language is not English, free will output localized text and parsing fails. Then add LANG=en_US.UTF-8 at the beginnning of the bash script. – Freddi Schiller May 19 '17 at 10:22
5

Another script that I wrote for this purpose:

#!/bin/bash
# Copyright 2019, Mikko Rantalainen
# License: MIT X License

# Minimum available memory until warning, default to 10% of total RAM (MiB)
THRESHOLD=$(grep "MemTotal:" /proc/meminfo | awk '{ printf "%d", 0.1*$2/1024}')
INTERVAL=60s

echo "Emitting a warning if less than $THRESHOLD MiB of RAM is available..."

while true; do
    meminfo=$(cat /proc/meminfo)
    free=$(echo "$meminfo" | grep "MemFree:" | awk '{ printf "%d", $2/1024}')
    available=$(echo "$meminfo" | grep "MemAvailable:" | awk '{ printf "%d", $2/1024}')
    inactive=$(echo "$meminfo" | grep "Inactive:" | awk '{ printf "%d", $2/1024}')
    reclaimable=$(echo "$meminfo" | grep "SReclaimable:" | awk '{ printf "%d", $2/1024}')
    usable=$(echo "$free + $inactive / 2 + $reclaimable / 2" | bc)
    if test -z "$available"; then
        message="Current kernel does not support MemAvailable in /proc/meminfo, aborting"
        notify-send "Error while monitoring low memory" "$message"
        echo "$message" 1>&2
        exit 1
    fi

    message="Available: $available MiB
Free: $free MiB
Maybe usable: $usable MiB"

    if [ "$available" -lt "$THRESHOLD" ]
        then
        notify-send -u critical "Low memory warning" "$message"
        echo "Low memory warning:"
    echo "$message"
    fi

    #echo "DEBUG: $message"
    sleep $INTERVAL
done
David Foerster
  • 36,264
  • 56
  • 94
  • 147
4

This cron job that runs every 1 min

* * * * * if [ $(free -wm | awk ' /^Mem:/ { print $8 } ') -le 400 ]; then XDG_RUNTIME_DIR=/run/user/$(id -u) notify-send 'Short on memory!'; fi

To add it you run crontab -e and paste it at the bottom. You have to have cron installed

  • Thanks for this, I for anyone else's benefit, I changed it slightly to make it stay on the screen until you click to get rid of so I don't accidentally miss any.

    * * * * * if [ $(free -wm | awk ' /^Mem:/ { print $8 } ') -le 400 ]; then XDG_RUNTIME_DIR=/run/user/$(id -u) notify-send -u critical -t 0 'Short on memory!'; fi

    – BT643 Sep 29 '21 at 08:35
3

Updated above script to also add details on top 3 memory-hungry processes. See at https://github.com/romanmelko/ubuntu-low-mem-popup

Here is the script itself:

#!/usr/bin/env bash

set -o errexit
set -o pipefail
set -o nounset

# If the language is not English, free will output localized text and parsing fails
LANG=en_US.UTF-8

THRESHOLD=500
INTERVAL=300
POPUP_DELAY=999999

# sleep some time so the shell starts properly
sleep 60

while :
do
    available=$(free -mw | awk '/^Mem:/{print $8}')
    if [ $available -lt $THRESHOLD ]; then
        title="Low memory! $available MB available"
        message=$(top -bo %MEM -n 1 | grep -A 3 PID | awk '{print $(NF - 6) " \t" $(NF)}')
        # KDE Plasma notifier
        kdialog --title "$title" --passivepopup "$message" $POPUP_DELAY
        # use the following command if you are not using KDE Plasma, comment the line above and uncomment the line below
        # please note that timeout for notify-send is represented in milliseconds
        # notify-send -u critical "$title" "$message" -t $POPUP_DELAY
    fi
    sleep $INTERVAL
done
  • Thank you for for contribution. The better practice here is to summarize (in this case copy) the content of the link you refer to. This way, your answer remains valid even if the link disappears. – Marc Vanhoomissen Mar 06 '19 at 14:07
2

Variant using RAM available, percentages and displays desktop notifications when called by cron (i.e. loop script doesn't have to be started after reboot):

#!/usr/bin/env bash

# dbus env var required when called via cron:
eval "export $(egrep -z DBUS_SESSION_BUS_ADDRESS /proc/$(pgrep -u $LOGNAME gnome-session)/environ | tr '\0' '\n')";

AVAIL_THRESHOLD=5

free_output=$(free)
mem_total=$(awk '/^Mem:/{print $2}' <<< $free_output)
mem_avail=$(awk '/^Mem:/{print $7}' <<< $free_output)
mem_avail_m=$(bc <<< "scale=1; $mem_avail/1024")
percent_avail=$(bc <<< "scale=1; $mem_avail*100 /$mem_total")
should_warn=$(bc <<< "$percent_avail < $AVAIL_THRESHOLD")

if (( $should_warn )); then
    notify-send "Memory warning - only $percent_avail% ($mem_avail_m MB) available"
else
    echo "Memory OK - $percent_avail% ($mem_avail_m MB) available"
fi
lambfrier
  • 123
1

Updated version of the script which works with free from procps-ng 3.3.10

#!/bin/bash

#Minimum available memory limit, MB
THRESHOLD=400

#Check time interval, sec
INTERVAL=30

while :
do
    free_out=$(free -w -m)
    available=$(awk '/^Mem:/{print $8}' <<<$free_out)

    if (( $available < $THRESHOLD ))
        then
        notify-send -u critical "Memory is running out!" "Available memory is $available MiB"
        echo "Warning - available memory is $available MiB"    
    fi

    cat <<<$free_out
    sleep $INTERVAL
done
0

I've created simple program which triggers linux notification and check ram interval of 5 seconds if RAM goes more than equal to 80 then it triggers notifications.

# Get cpu usage of last minitue
avg_cpu_use=$(uptime)
IFS=',' read -ra avg_cpu_use_arr <<<"$avg_cpu_use"
avg_cpu_use=""

for i in "${avg_cpu_use_arr[@]}"; do : if [[ $i == "load average" ]]; then avg_cpu_use=$i break fi done

avg_cpu_use=$(echo ${avg_cpu_use:16}) # Remove " load average: "

if [[ -z "${avg_cpu_use// /}" ]]; then avg_cpu_use="CPU: N/A%" exit -1 else avg_cpu_use="CPU: ${avg_cpu_use}%" fi

Get RAM usage

ram_use=$(free -m) IFS=$'\n' read -rd '' -a ram_use_arr <<<"$ram_use" ram_use="${ram_use_arr[1]}" ram_use=$(echo "$ram_use" | tr -s " ") IFS=' ' read -ra ram_use_arr <<<"$ram_use" total_ram="${ram_use_arr[1]}" ram_use="${ram_use_arr[2]}" ram_use="RAM: ${ram_use}/${total_ram} MB"

while true; do echo "Sys check....." base=80 ram_percent=$((ram_use_arr[2] * 100 / ram_use_arr[1])) if [ $ram_percent -ge $base ]; then zenity --notification --text "RAM: ${ram_percent}% ${avg_cpu_use}" else echo "All Good..." fi sleep 5 done

enter image description here