Skip to content

Instantly share code, notes, and snippets.

@JaseHadd
Last active November 25, 2025 13:43
Show Gist options
  • Select an option

  • Save JaseHadd/64567f103e3481a556426c98f3f83a4d to your computer and use it in GitHub Desktop.

Select an option

Save JaseHadd/64567f103e3481a556426c98f3f83a4d to your computer and use it in GitHub Desktop.
macOS App Memory Usage Checker
#!/bin/zsh
# This file can be included in your .zshrc, and then you can invoke it as 'app-mem',
# or you can save this file anywhere on your path, make it executable, and run it from
# any shell.
# The included function will gather all the processes for a given app and total up their
# memory usage, so you can check at a glance how much memory something is using.
app-mem()
{
local SUCCESS=0 USAGE_ERROR=1 NO_APP_FOUND=2 NO_PROCESSES=3 INTERNAL_ERROR=4
local flag_verbose flag_help flag_appname flag_bundle flag_processname flag_safari
local count identifier
local verbose=false
_APP_MEM_BASE_NAME=${_APP_MEM_BASE_NAME:-app-mem}
show_usage()
{
cat << EOF
Usage: $_APP_MEM_BASE_NAME [OPTIONS]
$_APP_MEM_BASE_NAME -h
Options:
-a, --app-name {identifier} Query by Application Name (e.g., Visual Studio Code). This uses the Spotlight
index to locate the application, and will find the same application as
'open -a identifier'.
-b, --bundle {identifier} Query by Bundle Identifier (e.g., com.microsoft.VSCode). This uses the spotlight
index to locate the application, and will find the same application as
'open -b identifier'.
-p, --process-name {identifier} Query by Process Name (e.g., Code). This searches the running processes for any
that match the provided name, and is the least precise method.
-s, --safari Query Safari browser processes. This is the only accurate way to get memory usage for
Safari, as Safari uses multiple processes for tabs and extensions.
-v, --verbose Enable verbose output
-h, --help Show this help message
EOF
}
zmodload zsh/zutil
if ! zparseopts -D -F -M -K -- \
{v,-verbose}=flag_verbose \
{h,-help}=flag_help \
{a,-app-name}=flag_appname \
{b,-bundle}=flag_bundle \
{p,-process-name}=flag_processname \
{s,-safari}=flag_safari; then
show_usage
return $USAGE_ERROR
fi
if [[ -n "$flag_help" ]]; then
show_usage
return $SUCCESS
fi
# number of modes specified
count=$(( ${#flag_appname} + ${#flag_bundle} + ${#flag_processname} + ${#flag_safari} ))
# convert verbose flag to boolean
[[ -n $flag_verbose ]] && verbose=true
if [[ $count -eq 0 ]]; then
# default to app name mode
flag_appname=true
fi
if [[ $count -gt 1 ]] \
|| ( [[ $# -eq 0 ]] && [[ -z "$flag_safari" ]] ) \
|| ( [[ $# -ne 0 ]] && [[ -n "$flag_safari" ]] ); then
# exit if multiple modes specified, or if incorrect number of arguments for the selected mode
show_usage
return $USAGE_ERROR
else
identifier="$@"
fi
format_kib()
{
local kib=$1
local number unit
bc -l << EOF | read unit number
input=$kib
gib = 1024 * (mib = 1024)
if (input < mib) { print "KiB "; i = input; }
else if (input < gib) { print "MiB "; i = input / mib; }
else { print "GiB "; i = input / gib; }
if (i < 10) { scale = 2; i / 1; }
else if (i < 100) { scale = 1; i / 1; }
else { scale = 0; i / 1; }
EOF
echo "$number $unit"
}
recurse_pid()
{
while read pid; do
echo "$pid"
ps -ax -oppid=,pid= \
| awk -v parent="$pid" '$1 == parent { print $2 }'
done
}
print_results()
{
local kib entries memory
local identifier="$1"
awk '{ print $1 }' | recurse_pid \
| while read pid; do; ps -o rss= -p "$pid" 2>/dev/null; done \
| awk '{sum+=$1; count++} END {print sum, count}' \
| read kib entries
if [[ $entries -eq 0 ]]; then
echo "No running processes found for '$identifier'"
return $NO_PROCESSES
fi
memory=$(format_kib $kib)
if [[ $verbose = true ]]; then
echo "Found $entries process(es) for '$identifier'"
echo "Total Memory Usage: $memory"
else
echo "$memory"
fi
}
if [ -n "$flag_appname" ]; then
local appPath
identifier="${identifier%.app}.app"
$verbose && echo "Searching by Application Name: $identifier"
appPath=$(mdfind "kMDItemKind == 'Application' && kMDItemFSName == '$identifier'" | head -n 1)
if [ -z "$appPath" ]; then
echo "No application found with name '$identifier'"
return $NO_APP_FOUND
fi
ps -ax -opid=,comm= | grep -i "$appPath" | print_results $identifier
return $?
elif [ -n "$flag_bundle" ]; then
$verbose && echo "Searching by Bundle Identifier: $identifier"
local appPath
appPath=$(mdfind "kMDItemCFBundleIdentifier == '$identifier'" | head -n 1)
if [ -z "$appPath" ]; then
echo "No application found with bundle identifier '$identifier'"
return $NO_APP_FOUND
fi
ps -ax -opid=,comm= | grep -i "$appPath" | print_results $identifier
return $?
elif [ -n "$flag_processname" ]; then
$verbose && echo "Searching by Process Name: $identifier"
ps -caxm -opid=,comm= | grep -i "$identifier" | print_results $identifier
return $?
elif [ -n "$flag_safari" ]; then
$verbose && echo "Querying Safari processes"
local asns=()
local pids=()
for asn in $(lsappinfo find displayname="Safari Networking"); do
asns+=("$asn")
asns+=($(lsappinfo find parentasn="$asn"))
done
asns+=($(lsappinfo find displayname="Safari Graphics and Media"))
asns+=($(lsappinfo find bundleid="com.apple.Safari"))
print -l -- $asns \
| xargs -n 1 lsappinfo info -only pid \
| awk -F'=' '{ print $2 }' \
| print_results "Safari"
return $?
else
echo "Internal error: No valid mode selected"
return $INTERNAL_ERROR
fi
}
if [[ "${ZSH_EVAL_CONTEXT}" == "toplevel" ]]; then
_APP_MEM_BASE_NAME=$(basename "$0")
app-mem "$@"
exit $?
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment