When a Linux server slows down, the first question is almost always the same: what is eating the CPU or the memory? This guide walks through the tools that answer that question—from the ones on every system to the specialized utilities worth installing—along with the gotchas that trip people up (like why “used” memory rarely means what you think).
Table of Contents
Quick answers
If you just need the command and want to move on:
# Top 10 processes by CPU
ps -eo pid,ppid,user,%cpu,%mem,comm --sort=-%cpu | head -n 11
# Top 10 processes by memory
ps -eo pid,ppid,user,%cpu,%mem,comm --sort=-%mem | head -n 11
The head -n 11 accounts for the header line plus 10 processes. Everything below explains what these numbers mean and when to reach for a better tool.
1. top — the tool that’s always there
top ships with virtually every Linux distribution, so it’s the reliable fallback:
top
By default top sorts by CPU usage. The interactive keys that matter most:
P— sort by CPU usage (the default)M— sort by memory usageN— sort by PIDT— sort by cumulative CPU timek— kill a process (you’ll be prompted for the PID)c— toggle between the command name and the full command line1— expand per-CPU-core statisticsq— quit
Note that these sort keys are uppercase. A common point of confusion: the %CPU column can exceed 100% for multi-threaded processes, because a value of 100% represents one fully saturated core. On an 8-core machine, a busy process can legitimately show up to 800%.
Running top non-interactively
For scripts, logs, or a quick one-shot snapshot, use batch mode:
# One snapshot, sorted by memory, top 15 lines
top -b -o +%MEM -n 1 | head -n 15
# Same idea for CPU
top -b -o +%CPU -n 1 | head -n 15
-bruns in batch mode (plain text, no screen control).-n 1takes a single iteration and exits.-osets the sort field.
2. htop — a friendlier, interactive top
htop is not always preinstalled, but it’s worth adding:
# Debian/Ubuntu
sudo apt install htop
# RHEL/Fedora/CentOS
sudo dnf install htop
It gives you colored per-core meters, scrolling, mouse support, and easy sorting. Inside htop:
F6(or click a column header) — choose the sort fieldF4— filter processes by nameF3— searchF5— toggle the process tree viewF9— send a signal (kill)H— hide/show user threadsu— filter by a specific user
The tree view (F5) is particularly useful for spotting a parent process that has spawned many children—a frequent cause of runaway resource usage.
See also: Mastering the Linux Command Line — Your Complete Free Training Guide
3. ps — the scriptable snapshot
ps is ideal when you want a precise, one-time list you can pipe into other commands. Two equivalent, widely used forms:
# BSD-style syntax, sorted by CPU
ps aux --sort=-%cpu | head
# Explicit column selection, sorted by memory
ps -eo pid,ppid,user,%mem,%cpu,rss,comm --sort=-%mem | head
Useful column choices for -eo:
pid,ppid— process and parent process IDs%cpu,%mem— percentage of CPU and physical memoryrss— resident set size in KB (actual physical RAM used)vsz— virtual memory size in KB (usually much larger; see the caveats below)comm— the command name onlyargs(orcmd) — the full command line, helpful when many processes share a name
The leading - in --sort=-%cpu means descending order. Drop it for ascending.
A caveat about %CPU in ps
Unlike top, the %CPU reported by ps is an average over the entire lifetime of the process, not an instantaneous reading. A process that hammered the CPU at startup and is now idle may still show a high number. For a live view, top, htop, or pidstat are better.
4. pidstat — sampling over time
When you need to see which process is busy right now and how that changes, pidstat (from the sysstat package) samples at an interval:
sudo apt install sysstat # Debian/Ubuntu
sudo dnf install sysstat # RHEL/Fedora
# CPU usage, refreshed every 1 second
pidstat -u 1
# Memory usage, every 2 seconds
pidstat -r 2
# Both CPU and memory for a specific PID, every second
pidstat -u -r -p 1234 1
Because pidstat measures deltas between samples, its CPU numbers reflect current activity rather than a lifetime average—closer to what top shows.
5. Getting accurate memory figures
This is where most quick answers go wrong. The RSS (resident set size) reported by ps and top counts shared memory—like shared libraries—against every process that maps it. If ten processes each link libc, that memory is counted ten times. Summing RSS therefore overstates real usage.
The more honest metric is PSS (proportional set size), which divides shared pages evenly among the processes using them. The smem tool reports it:
sudo apt install smem # Debian/Ubuntu
sudo dnf install smem # RHEL/Fedora
# Processes sorted by PSS, human-readable sizes
smem -rk
# Aggregate by application name instead of per-process
smem -rk -P <name>
You can also read a single process’s resident memory straight from /proc:
grep VmRSS /proc/1234/status
For a system-wide picture of memory (not per process), use:
free -h
Pay attention to the available column rather than free. Linux deliberately uses spare RAM for the page cache, so “free” memory is usually low by design; “available” estimates what applications can actually claim without swapping.
6. atop and glances — richer monitors
Two more tools go beyond CPU and memory to give a fuller system view:
atoprecords historical data and can highlight resource-heavy processes, including disk and network I/O per process. It can even replay past activity from its logs—useful for diagnosing an incident after the fact.glancesis a cross-platform monitor (written in Python) that shows CPU, memory, disk, network, and per-process stats on one screen, with optional web and API modes.
sudo apt install atop glances
7. Memory and CPU by control group
On modern systems where services run under systemd, resource usage is often best understood per cgroup (i.e., per service) rather than per process:
systemd-cgtop
This sorts control groups by CPU, memory, or I/O and makes it obvious when a single service—rather than a lone process—is responsible for the load. Inside containers, the same idea applies; tools like docker stats expose per-container CPU and memory.
Putting it together: a practical workflow
- Start broad. Run
toporhtopto see whether the pressure is CPU, memory, or both, and whether it’s one process or many. - Confirm CPU over time. If CPU is the issue, use
pidstat -u 1to watch live usage and identify the persistent offender (not just a startup spike). - Get memory right. If memory is the issue, don’t trust summed
RSS. Usesmem -rkfor PSS, and checkfree -h‘s available column for the real headroom. - Attribute to a service. Use
systemd-cgtop(ordocker stats) to tie the usage back to a service or container rather than a bare PID. - Act deliberately. Once you’ve identified the culprit, decide whether to renice it (
renice), limit it (cgroups / systemd resource controls), restart the service, or fix the underlying cause—killing a process withkillis a last resort, not a fix.
Summary
| Goal | Best tool | Command |
|---|---|---|
| Quick interactive look | top | top (press P or M) |
| Nicer interactive UI | htop | htop |
| Scriptable snapshot | ps | ps aux --sort=-%cpu | head |
| Live CPU over time | pidstat | pidstat -u 1 |
| Accurate memory (PSS) | smem | smem -rk |
| System memory overview | free | free -h |
| Per-service usage | systemd-cgtop | systemd-cgtop |
The tools on every Linux box—top, ps, and free—will answer the question most of the time. The specialized ones (pidstat, smem, atop, systemd-cgtop) matter when you need accuracy over time or an honest accounting of shared memory. Knowing which number you’re actually looking at is what turns a guess into a diagnosis.


