How to find what is using CPU and memory¶
A server is slow, its load average is high, or it's running short of memory, and you need to know which processes are responsible.
This guide goes from a quick overview to the individual processes and services using CPU and memory, with tools that are on every distribution (top, ps, /proc) and pidstat from the sysstat package for measurements over an interval.
What the load average and the memory figures mean is explained in Load average and memory; the columns used here are listed in Process information fields.
Before you begin¶
- You need a shell on the machine. Any user can see every process's CPU and memory use;
sudois needed to read the kernel log and some details of other users' processes. - Installing
sysstatneedssudoand access to the distribution's package repositories.
Steps¶
-
Get an overview: how long the machine has been up, the load average, the number of CPUs, and memory:
$ uptime 13:40:12 up 2 days, 3:12, 1 user, load average: 1.08, 0.97, 0.62 $ nproc 2 $ free -h total used free shared buff/cache available Mem: 1.9Gi 643Mi 1.1Gi 5.1Mi 375Mi 1.3Gi Swap: 0B 0B 0BCompare the load average with the number of CPUs: a load close to or above
nprocmeans work is queueing. For memory, read theavailablecolumn, notfree: page cache inbuff/cacheis given back when programs need it. -
Take a snapshot of the busiest processes with
topin batch mode, sorted by CPU:$ top -b -n 1 -o %CPU | sed -n '1,12p' top - 13:40:15 up 2 days, 3:12, 1 user, load average: 1.08, 0.97, 0.62 Tasks: 121 total, 2 running, 119 sleeping, 0 stopped, 0 zombie %Cpu(s): 50.0 us, 0.0 sy, 0.0 ni, 50.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st MiB Mem : 1974.2 total, 1144.6 free, 643.0 used, 375.3 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 1331.2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1153 root 20 0 2584 1536 1408 R 100.0 0.1 2:01.03 report-+ 1155 root 20 0 398528 392448 3200 S 0.0 19.4 0:01.12 cache-l+ 1 root 20 0 24092 14500 10740 S 0.0 0.7 0:01.60 systemdsed -n '1,12p'keeps the first 12 lines of output. In the header,%Cpu(s)splits CPU time into user (us), system (sy), nice (ni), idle (id), waiting for I/O (wa), interrupts (hi,si) and time taken by the hypervisor (st, "steal"). In the process list,%CPUis the share of one CPU over the last interval, so a single process can show up to 100 per CPU it uses, andRESis the resident memory in KiB.Run plain
topto watch the same view refresh every few seconds. Inside it, pressPto sort by CPU,Mto sort by memory,1to show each CPU separately,cto show full command lines,Hto show threads, andqto quit. -
List processes sorted by resident memory, with
ps:$ ps -eo pid,ppid,user,rss,vsz,%mem,comm --sort=-rss | sed -n '1,6p' PID PPID USER RSS VSZ %MEM COMMAND 1155 1 root 392448 398528 19.4 cache-loader 328 1 root 14500 42728 0.7 systemd-journal 1 0 root 14500 24092 0.7 systemd 827 1 labuser 11956 21796 0.5 systemd 872 870 labuser 5376 8232 0.2 bashRSS(resident set size, in KiB) is the physical memory a process is using now, including shared libraries it maps;VSZis the address space it has reserved, which is usually much larger and rarely matters. To sort by CPU instead, use--sort=-%cpu; but note that the%CPUcolumn ofpsis the average over the process's whole lifetime, not its current use. For current use, usetop(step 2) orpidstat(step 5). -
Look at one process in detail. Replace
<PID>with a process ID from the previous steps:$ ps -o pid,ppid,user,stat,etime,time,args -p <PID> PID PPID USER STAT ELAPSED TIME COMMAND 1155 1 root S 02:03 00:00:01 /bin/bash /usr/local/bin/cache-loader $ grep -e '^State' -e '^VmRSS' -e '^RssAnon' -e '^RssFile' -e '^VmSwap' -e '^Threads' /proc/<PID>/status State: S (sleeping) VmRSS: 392448 kB RssAnon: 390784 kB RssFile: 1664 kB VmSwap: 0 kB Threads: 1 $ cat /proc/<PID>/cgroup 0::/user.slice/user-1000.slice/session-4.scopeargsshows the full command line, which tells apart processes with the same name.RssAnonis memory only the process itself can free (heap and stack);RssFileis mapped files, which the kernel can drop and re-read./proc/<PID>/cgroupshows which service or session the process belongs to: a path ending in.serviceon systemd, oropenrc.<SERVICE>on Gentoo with OpenRC.
-
Measure CPU and memory use over an interval with
pidstat. Install thesysstatpackage if it isn't installed already:Ubuntu's cloud images have
sysstatinstalled already.-Syuupdates the whole system along with installing the package, because Arch doesn't support installing new packages against an outdated system.Then sample every process's CPU use once a second, five times:
$ pidstat -u 1 5 Linux 6.12.115+deb13.1-cloud-amd64 (server1) 11/07/26 _x86_64_ (2 CPU) 13:41:02 UID PID %usr %system %guest %wait %CPU CPU Command 13:41:03 0 1153 100.00 0.00 0.00 0.00 100.00 0 report-builder … Average: UID PID %usr %system %guest %wait %CPU CPU Command Average: 0 1153 100.00 0.00 0.00 0.00 100.00 - report-builderpidstatlists only processes that used CPU during each interval, which makes busy processes easy to spot.%usrand%systemsplit the time between the program's own code and work the kernel did for it; a high%waitmeans the process was ready to run but waiting for a CPU. Other useful views:pidstat -r 1 5: memory, including page faults per second. A highmajflt/smeans the process is waiting for pages to be read from disk or swap.pidstat -d 1 5: disk reads and writes per process (as root, to see every process).pidstat -t -p <PID> 1 5: each thread of one process.
-
Find out which service is using the resources.
systemd-cgtopshows CPU and memory per service, slice and session, refreshed liketop; in batch mode it prints one snapshot:$ systemd-cgtop -b -n 1 --order=memory | sed -n '1,6p' user.slice 10 - 497.6M - - user.slice/user-1000.slice 10 - 497.6M - - / 118 - 430.8M - - user.slice/user-1000.slice/session-4.scope 3 - 193.3M - - system.slice 12 - 109.8M - - system.slice/systemd-journald.service 1 - 12.2M - -The columns are the cgroup, its number of tasks, CPU use, memory, and I/O rates. In batch mode the first snapshot has no CPU figures (
-), because CPU use is measured between two refreshes; runsystemd-cgtopwithout options to watch it refresh, and pressc,mortto sort by CPU, memory or number of tasks.systemctl status <SERVICE>shows the same memory and CPU figures for one service, followed by its processes.systemd-cgtopshows CPU and memory per service, slice and session, refreshed liketop; in batch mode it prints one snapshot:$ systemd-cgtop -b -n 1 --order=memory | sed -n '1,6p' user.slice 10 - 497.6M - - user.slice/user-1000.slice 10 - 497.6M - - / 118 - 430.8M - - user.slice/user-1000.slice/session-4.scope 3 - 193.3M - - system.slice 12 - 109.8M - - system.slice/systemd-journald.service 1 - 12.2M - -The columns are the cgroup, its number of tasks, CPU use, memory, and I/O rates. In batch mode the first snapshot has no CPU figures (
-), because CPU use is measured between two refreshes; runsystemd-cgtopwithout options to watch it refresh, and pressc,mortto sort by CPU, memory or number of tasks.systemctl status <SERVICE>shows the same memory and CPU figures for one service, followed by its processes.systemd-cgtopshows CPU and memory per service, slice and session, refreshed liketop; in batch mode it prints one snapshot:$ systemd-cgtop -b -n 1 --order=memory | sed -n '1,6p' user.slice 10 - 497.6M - - user.slice/user-1000.slice 10 - 497.6M - - / 118 - 430.8M - - user.slice/user-1000.slice/session-4.scope 3 - 193.3M - - system.slice 12 - 109.8M - - system.slice/systemd-journald.service 1 - 12.2M - -The columns are the cgroup, its number of tasks, CPU use, memory, and I/O rates. In batch mode the first snapshot has no CPU figures (
-), because CPU use is measured between two refreshes; runsystemd-cgtopwithout options to watch it refresh, and pressc,mortto sort by CPU, memory or number of tasks.systemctl status <SERVICE>shows the same memory and CPU figures for one service, followed by its processes.With OpenRC, each service has a cgroup named
openrc.<SERVICE>, and the kernel keeps its totals inmemory.current(bytes) andcpu.stat(microseconds of CPU time since the service started). List the memory use of every service, largest first:$ grep -H . /sys/fs/cgroup/openrc.*/memory.current | sort -t: -k2 -rn /sys/fs/cgroup/openrc.sshd/memory.current:421105664 /sys/fs/cgroup/openrc.udev/memory.current:9113600 /sys/fs/cgroup/openrc.sysklogd/memory.current:1093632Programs started from an SSH login stay in the cgroup of
sshd, so on a server they are counted underopenrc.sshd.In
systemd-cgtop,user.sliceholds everything started from login sessions; a service that is busy appears undersystem.slicewith its unit name. Memory per cgroup includes the page cache its processes caused, so a service that reads large files can show more memory than the sum of its processes'RSS. -
If memory ran out, check whether the kernel's OOM killer ended a process:
Each OOM kill logs a line such as
Out of memory: Killed process 1155 (cache-loader), orMemory cgroup out of memorywhen a service reached its own memory limit. The kernel log in memory covers the current boot only; on systemd systems with a persistent journal,journalctl -k -b -1shows the previous boot's kernel messages, and on Gentoo with OpenRC,sysklogdwrites kernel messages to/var/log/kern.logand/var/log/syslog(OOM reports are logged at error level, so they don't appear in/var/log/messages). -
If the load is high but the CPUs are mostly idle, look for processes in uninterruptible sleep (state
D), which are counted in the load average while they wait for I/O:The
WCHANcolumn names the kernel function each process is waiting in. The same few processes staying inDstate for a long time usually point to a slow or hung disk, or an unreachable network filesystem.
Verify¶
You've found the cause when the processes you identified account for the load or memory use you saw in step 1: their %CPU values in top or pidstat add up to roughly the busy CPU time in the %Cpu(s) line, or their memory accounts for the drop in available.
After you stop or limit them, top and free -h should show the change within a few seconds, and the 1-minute load average within a minute or two.
Troubleshooting¶
pidstat prints only its header lines.
No process used CPU during the interval, so there was nothing to list; on an idle machine that's expected.
Use a longer interval, or pidstat -p ALL to list every process whether it was active or not.
top shows high st (steal).
The hypervisor is giving the virtual machine's CPU time to other guests.
No process inside the machine is responsible; the fix is on the host or with the provider.
%CPU in top adds up to more than 100.
top reports each process as a share of one CPU, so a multi-threaded process on a machine with 8 CPUs can show up to 800.
Press I (Irix mode off) to show shares of the whole machine instead.
Memory use grows but no process has a large RSS.
Look at the rest of /proc/meminfo: large Shmem values point to tmpfs filesystems (check with df -h -t tmpfs), and a large SUnreclaim points to kernel memory, often from a driver or a very large number of network connections or files.
Related¶
- Explanation: Load average and memory
- Explanation: Processes and signals
- Explanation: Control groups
- Reference: Process information fields
- Reference: Signals
- How-to: How to limit a service's resources
- How-to: How to change kernel parameters persistently