How to Find and Fix RAM Leaks on Linux Desktops (2026)

Arjun’s Systems Brief: I have spent years tuning memory-constrained systems in production. When a Linux desktop starts slowing down after hours of use, the first instinct is to blame the hardware. It is almost never the hardware. A process is leaking memory, and I will show you exactly how to find RAM leaks, prove they are leaking, and fix them without rebooting your entire system.

Why Your Linux Desktop Gets Slower Over Time

Every Linux user hits this moment. You boot up your desktop, everything feels snappy. Six hours later, switching between windows takes forever, your browser stutters, and htop shows 95% memory usage. The natural reaction is to think your RAM is insufficient. In most cases, that is wrong.

Linux aggressively uses free RAM for file caching. When you open a video file, the kernel loads it into memory. When you close the player, that data stays in RAM until something else needs it. This is by design. The kernel believes unused RAM is wasted RAM. So before you panic about high memory usage, you need to understand what “used” actually means.

There are two fundamentally different problems that look identical from the outside:

    • High cache usage: The kernel is caching files efficiently. Your RAM is being utilized for performance. This is healthy.
    • Memory leak: A process is allocating memory and never releasing it. Over hours or days, it consumes more and more RAM until the system thrashes. This is the problem I will teach you to diagnose.

The difference matters because the fix for high cache usage is “do nothing” while the fix for a memory leak is “find the culprit and kill it.” Mixing these up leads to terrible advice like automatically clearing your RAM cache every hour, which I covered in my guide to clearing RAM cache. That advice was wrong then and it is wrong now.

When you run free -h, look at the “available” column, not “free.” The available column shows memory that is genuinely available for new processes, including reclaimable cache. If available is above 20% of total, your system is not memory-starved.

Step 1: Spot the Suspect Process

The first step is identifying which process is consuming the most memory. I use three tools in combination because each one reveals something different.

top: Real-Time Memory Ranking

The top command sorted by memory gives you an instant snapshot of who is eating your RAM. The -o %MEM flag sorts by memory usage instead of CPU, which is exactly what you want for leak investigation.

fosslinux@ubuntu:~$ top -o %MEM | head -20
top - 23:41:22 up 3 days, 14:27,  0 users,  load average: 0.23, 0.22, 0.09
Tasks: 301 total,   1 running, 300 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  2.3 sy,  0.0 ni, 97.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st 
MiB Mem :   3350.6 total,    351.5 free,   1044.6 used,   2241.3 buff/cache     
MiB Swap:   7958.0 total,   7923.1 free,     34.9 used.   2306.0 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   2386 gdm-gre+  20   0 4766636 188684 110028 S   0.0   5.5   1:04.57 gnome-s+
   2941 gdm-gre+  20   0  755456  95356  85280 S   0.0   2.8   0:00.76 mutter-+
   2931 gdm-gre+  20   0  241448  74764  63844 S   0.0   2.2   0:00.18 Xwayland
   2990 gdm-gre+  20   0  390384  69644  62904 S   0.0   2.0   0:00.30 ibus-x11
   2965 gdm-gre+  20   0  707376  44472  34572 S   0.0   1.3   0:02.05 xdg-des+

Look at the RES column, not VIRT. VIRT includes memory that is allocated but not actually resident in physical RAM. RES shows what is actually consuming your physical memory. In this capture, gnome-shell is using 188MB of real RAM, which is normal for a GNOME desktop session.

ps: Snapshot for Comparison

The ps command with sort-by-RSS gives you a cleaner snapshot that is easier to log over time:

fosslinux@ubuntu:~$ ps aux --sort=-rss | head -15
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
gdm-gre+    2386  0.0  5.4 4766636 188684 ?      Ssl  Aug04   1:04 /usr/bin/gnome-shell --mode=gdm
gdm-gre+    2941  0.0  2.7 755456 95356 ?        Sl   Aug04   0:00 /usr/libexec/mutter-x11-frames
gdm-gre+    2931  0.0  2.1 241448 74764 ?        S    Aug04   0:00 /usr/bin/Xwayland :1024 -rootless
gdm-gre+    2990  0.0  2.0 390384 69644 ?        Sl   Aug04   0:00 /usr/libexec/ibus-x11
gdm-gre+    2965  0.0  1.2 707376 44472 ?        Ssl  Aug04   0:02 /usr/libexec/xdg-desktop-portal-gnome
root        3646  0.0  1.2 521540 42732 ?        Ssl  Aug04   0:11 /usr/libexec/fwupd/fwupd
root       64367  0.0  0.8 2073284 28824 ?       Ssl  Aug05   0:16 /snap/snapd/current/usr/lib/snapd/snapd

I run this command every hour and save the output to a file. If a process’s RSS number keeps climbing across multiple snapshots, that is your leak suspect.

smem: The Accurate Memory View

The smem tool is the most accurate way to measure per-process memory because it uses PSS (Proportional Set Size) instead of RSS. The difference matters:

      • RSS counts the full size of shared libraries for every process that uses them. If three processes share libc.so, RSS counts it three times.
      • PSS divides shared library usage by the number of sharers. It gives you the “fair share” of memory that each process actually uses.
fosslinux@ubuntu:~$ smem -t -p -s pss | head -20
  PID User     Command                         Swap      USS      PSS      RSS 
91676 fosslinux /bin/bash /tmp/vmware-fossl    0.00%    0.01%    0.03%    0.11% 
91678 fosslinux /bin/bash /tmp/full_capture    0.00%    0.01%    0.04%    0.12% 
91705 fosslinux head -20                       0.00%    0.15%    0.16%    0.23% 
91704 fosslinux /usr/bin/python3 /usr/bin/s    0.00%    0.37%    0.38%    0.47%

Insight: RSS is useful for a quick look, but PSS is what you want for accurate memory accounting. A process showing 500MB RSS might only be using 150MB of PSS if it shares most of its libraries with other processes. When tracking leaks, always use PSS over time.

Step 2: Track Memory Over Time (The Leak Test)

A single snapshot cannot prove a leak. You need to track memory usage over time and look for a pattern. A leak shows up as a steady, unbroken climb in RSS or PSS that never decreases. Normal memory usage fluctuates as the kernel caches and evicts data.

The Quick Manual Check

I start with a manual check. I grab the PID of the suspect process and poll it every minute:

fosslinux@ubuntu:~$ GS_PID=$(pgrep -o gnome-shell)
fosslinux@ubuntu:~$ cat /proc/$GS_PID/status | grep -E 'VmRSS|VmSize|VmSwap'
VmSize:	 4766636 kB
VmRSS:	  188684 kB
VmSwap:	    8880 kB

Here, VmSize is the total virtual memory (including mappings that are not backed by physical pages), VmRSS is the actual physical memory in use, and VmSwap is memory that has been pushed to swap. If VmRSS grows by 5-10MB every hour without any user action, you have a leak.

The Automated Monitor Script

For a proper leak test, I use a simple bash script that logs memory usage to a CSV file. I let it run for 2-4 hours while doing normal work:

fosslinux@ubuntu:~$ cat monitor_mem.sh
#!/bin/bash
# Simple memory leak monitor script
PID=$1
INTERVAL=${2:-60}
LOGFILE="mem_log_${PID}.csv"
echo "timestamp,pid,rss_kb,vsz_kb" > $LOGFILE
echo "Monitoring PID $PID every ${INTERVAL}s. Press Ctrl+C to stop."
while kill -0 $PID 2>/dev/null; do
    TIMESTAMP=$(date +%H:%M:%S)
    ...
    sleep $INTERVAL
done
echo "Process $PID no longer running. Log saved to $LOGFILE"

Running it against gnome-shell:

fosslinux@ubuntu:~$ bash monitor_mem.sh 2386 2
23:41:26,2386,188684,4766636
23:41:26 | RSS: 188684KB | VSZ: 4766636KB
23:41:27,2386,188684,4766636
23:41:27 | RSS: 188684KB | VSZ: 4766636KB
23:41:28,2386,188684,4766636
23:41:28 | RSS: 188684KB | VSZ: 4766636KB

After a few hours, import the CSV into any spreadsheet tool and plot the RSS column over time. A leak produces a clear upward slope. Healthy memory usage produces a flat or oscillating line.

sar: Historical System-Wide Data

If you suspect a system-wide memory issue rather than a single process, sar from the sysstat package gives you historical data:

fosslinux@ubuntu:~$ sar -r 1 3
Linux 7.0.0-22-generic (ubuntu) 	08/07/2026 	_x86_64_	(4 CPU)

11:41:22 PM kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
11:41:23 PM    354876   2356496    556064     16.21     67348   1986704   2859284     24.69    277624   2083576       188
11:41:24 PM    355952   2357676    554884     16.17     67348   1986792   2859284     24.69    277532   2083664       188
11:41:25 PM    355952   2357676    554876     16.17     67356   1986792   2859284     24.69    277532   2083664       216
Average:       355593   2357283    555275     16.18     67351   1986763   2859284     24.69    277563   2083635       197

The kbmemused column should stay relatively stable over time. If it climbs steadily day after day, something is leaking at the system level.

Why It Matters: I once spent three hours debugging a “memory leak” that turned out to be a web browser with 200 open tabs. Before you blame a kernel bug or a daemon, count your browser tabs and background applications. Chromium-based browsers are the number one cause of high memory usage on Linux desktops.

Step 3: Deep-Dive the Suspect Process

Once you have identified the suspect process, you need to understand what it is doing with all that memory. The pmap tool and /proc/PID/smaps give you a granular breakdown.

pmap: Memory Map Breakdown

The pmap -x command shows every memory mapping for a process, including which libraries are loaded, how much heap is allocated, and how much stack is in use:

fosslinux@ubuntu:~$ pmap -x 2386 | head -30
2386:   /usr/bin/gnome-shell --mode=gdm
Address           Kbytes     RSS   Dirty Mode  Mapping
0000000000400000     864     512     512 r-x-- gnome-shell
00000000006d8000      32      32      32 rw--- gnome-shell
00000000006e0000    3200    2176    2176 rw---   [ anon ]
...

The key columns are:

      • Mapping: Which file or anonymous region this memory belongs to
      • RSS: How much of this mapping is actually in physical RAM
      • Dirty: How much has been modified (private to this process)
      • Mode: r-x means read-execute (code), rw means read-write (data/heap)

If you see a large anonymous [anon] mapping that keeps growing in RSS across multiple pmap snapshots, that is likely where the leak lives.

/proc/PID/smaps: The Full Picture

The smaps file gives you the most detailed view of a process’s memory. It shows every mapping with 20+ metrics. The ones that matter for leak detection:

fosslinux@ubuntu:~$ sudo cat /proc/2386/smaps | head -50
c6c8e68000-c6c8e75000 r-xp 00000000 00:00 0  [anon:js-executable-memory]
Size:                 52 kB
Rss:                  52 kB
Pss:                  52 kB
Private_Dirty:        52 kB
Anonymous:            52 kB
Swap:                  0 kB

Private_Dirty is the number you want to watch. It represents memory that is both private to this process and has been modified. This is the memory that cannot be reclaimed without killing the process. If Private_Dirty keeps growing, that is your leak.

smem: Library-Level Breakdown

The smem -m command shows you which libraries and memory regions are consuming the most PSS:

fosslinux@ubuntu:~$ smem -m -P gnome-shell | head -20
Map                                       PIDs   AVGPSS      PSS 
[heap]                                       1     3280     3280 
/usr/lib/x86_64-linux-gnu/libc.so.6          1      215      215 
/usr/lib/x86_64-linux-gnu/libm.so.6          1      195      195 
/usr/lib/locale/locale-archive               1       82       82 
[stack]                                      1       84       84 
/usr/lib/python3.14/lib-dynload/_zstd.cp     1       60       60 
/usr/lib/x86_64-linux-gnu/libzstd.so.1.5     1       58       58 
/usr/lib/python3.14/lib-dynload/_lzma.cp     1       48       48 
/usr/lib/x86_64-linux-gnu/ld-linux-x86-6     1       42       42 
/usr/lib/python3.14/lib-dynload/_bz2.cpy     1       36       36 
/usr/lib/x86_64-linux-gnu/libz.so.1.3.1      1        9        9

The [heap] entry is where malloc allocations land. If heap PSS keeps growing, the application is allocating memory and not freeing it. That is a textbook leak.

Step 4: Desktop-Specific Leak Culprits

After years of debugging Linux desktop memory issues, I can tell you that 90% of the leaks come from the same five sources. Here is my hit list, in order of frequency.

1. GNOME Shell Extensions

GNOME Shell extensions run inside the gnome-shell process. A buggy extension can leak memory that grows unbounded until your entire desktop becomes sluggish. I have seen extensions that leak 50MB per day of normal use.

Diagnosis: Disable all extensions, restart GNOME Shell, and monitor RSS for a few hours. If the leak disappears, re-enable extensions one by one until you find the culprit.

Fix: Run gnome-extensions list to see installed extensions. Disable them with gnome-extensions disable [extension-id]. Restart GNOME Shell with Alt+F2, type r, press Enter (X11 only; on Wayland, log out and back in).

2. Firefox Content Processes

Firefox uses separate content processes for tabs. Each process consumes memory, and some processes leak memory over time, especially with complex web applications like Google Docs or Slack web.

Diagnosis: Open about:memory in Firefox and click “Measure.” Look for content processes with unusually high memory compared to others.

Fix: Close unused tabs. In about:config, set dom.ipc.processCount to limit the number of content processes. Restart Firefox periodically if you are a heavy tab user.

3. Electron Apps (VS Code, Slack, Discord)

Electron apps are Chromium-based, which means they inherit Chromium’s memory characteristics. Each Electron app spawns multiple processes, and extensions or workspace configurations can cause leaks.

Diagnosis: Check memory usage of all processes belonging to the app:

fosslinux@ubuntu:~$ ps aux | grep -E 'code|slack|discord' | awk '{print $2, $6}'

Fix: Disable unnecessary extensions. For VS Code, open the Command Palette and run “Developer: Restart Extension Host.” For Slack, disable hardware acceleration in settings. Restart the app daily if memory usage concerns you.

4. PipeWire/WirePlumber

The PipeWire audio daemon can leak memory on certain hardware configurations, particularly with USB audio devices or Bluetooth headphones that connect and disconnect frequently.

Diagnosis: Monitor the pipewire process RSS over a few hours of audio playback.

Fix: Restart the audio stack: systemctl --user restart pipewire pipewire-pulse wireplumber

5. Xorg/Wayland Compositors

The display server itself can leak memory, especially with multiple monitors or high-resolution displays. This is less common in 2026 than it was a few years ago, but it still happens.

Diagnosis: Monitor the Xwayland or Xorg process RSS.

Fix: This usually requires a full desktop session restart (log out and log back in).

Worth Knowing: The OOM (Out of Memory) killer is Linux’s last line of defense. When memory gets critically low, the kernel kills the process with the highest oom_score. You can check a process’s score with cat /proc/PID/oom_score. If your leaky process keeps getting killed, the OOM killer is doing its job. The real fix is stopping the leak, not disabling the OOM killer.

Step 5: Fix the Leak

Finding the leak is only half the battle. Here is how to fix the most common causes.

Restart the Leaking Process

The simplest fix is to restart the leaking process. Use kill PID (SIGTERM) first, which gives the process a chance to clean up. Only use kill -9 PID (SIGKILL) if the process is unresponsive.

fosslinux@ubuntu:~$ kill 2386  # Graceful shutdown
fosslinux@ubuntu:~$ kill -9 2386  # Force kill if unresponsive

For GNOME Shell, restarting kills your entire desktop session, so save your work first. On X11, you can restart just the Shell with Alt+F2 then r. On Wayland, you need to log out and log back in.

Disable Problematic Extensions

For GNOME Shell leaks caused by extensions:

fosslinux@ubuntu:~$ gnome-extensions list
[email protected]

fosslinux@ubuntu:~$ gnome-extensions disable [email protected]

Disable extensions one at a time, restart GNOME Shell, and monitor memory. This is the only reliable way to identify which extension is leaking.

Firefox Memory Optimization

Open about:memory in Firefox and click “Minimize memory usage.” This forces Firefox to release memory from inactive tabs. For persistent issues, set dom.ipc.processCount to 4 in about:config to limit content processes.

Electron App Fixes

For VS Code: Disable extensions you do not use. Open the Extensions view (Ctrl+Shift+X) and disable or uninstall anything you have not used in the past week. Also disable GPU acceleration if you notice memory growth: add "disable-hardware-acceleration": true to your settings.

For Slack and Discord: Disable hardware acceleration in Settings > Advanced. This prevents GPU memory leaks that are common with Electron apps.

Restart Audio Services

If PipeWire is leaking:

fosslinux@ubuntu:~$ systemctl --user restart pipewire pipewire-pulse wireplumber

This restarts the entire audio stack without affecting your desktop session. Audio will briefly cut out and resume automatically.

Step 6: Prevent Future Leaks

Fixing leaks after they happen is reactive. Here is how to prevent them from causing problems in the first place.

systemd Resource Limits

You can cap memory usage per application using systemd’s MemoryMax directive. This does not fix the leak, but it prevents a single leaky process from killing your entire system:

fosslinux@ubuntu:~$ systemctl --user edit firefox.service
[Service]
MemoryMax=4G
MemoryHigh=3G

When Firefox exceeds 3GB, the kernel starts reclaiming memory aggressively. At 4GB, it gets OOM-killed. This protects your system while you investigate the root cause.

Pro Tip: For desktop applications, MemoryMax is more useful than MemoryHigh. MemoryHigh triggers kernel reclaim, which can make the application sluggish. MemoryMax triggers OOM kill, which is a clean restart. For a browser, a clean restart is better than a sluggish 10 minutes.

Automated Monitoring

Set up a simple cron job that monitors your most leak-prone processes and alerts you when memory usage crosses a threshold:

fosslinux@ubuntu:~$ crontab -e
# Check Firefox memory every 30 minutes, alert if RSS > 3GB
*/30 * * * * RSS=$(ps -C firefox -o rss= | awk '{sum+=$1} END {print sum}'); if [ $RSS -gt 3145728 ]; then notify-send "Firefox memory alert" "RSS: $((RSS/1024))MB exceeds 3GB"; fi

Keep Your System Updated

Memory leaks are bugs. Bug fixes come with updates. Keep your desktop environment, browser, and applications updated. Most distribution update managers will notify you of available updates.

Deep Dive: Using Valgrind for Application Developers

If you are developing applications and need to find the exact source of a memory leak, Valgrind’s Memcheck tool is the gold standard. It instruments your program at runtime and reports every allocation that is not freed.

fosslinux@ubuntu:~$ gcc -g -o leak_test leak_test.c
fosslinux@ubuntu:~$ valgrind --leak-check=yes ./leak_test
==91727== Memcheck, a memory error detector
==91727== Copyright (C) 2002-2024, and GNU GPL'd, by Julian Seward et al.
==91727== Using Valgrind-3.26.0 and LibVEX; rerun with -h for copyright info
==91727== Command: /tmp/leak_test
Allocated 100 ints at 0x4aa0040
==91727== HEAP SUMMARY:
==91727==     in use at exit: 400 bytes in 1 blocks
==91727==   total heap usage: 2 allocs, 1 frees, 4,496 bytes allocated
==91727== 400 bytes in 1 blocks are definitely lost in loss record 1 of 1
==91727==    at 0x4850858: malloc (vg_replace_malloc.c:447)
==91727== LEAK SUMMARY:
==91727==    definitely lost: 400 bytes in 1 blocks
==91727== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

The output tells you exactly where the leak is: leaky_function at line 5 of leak_test.c called malloc but never freed it. Compile with -g to get line numbers in the report.

Valgrind vs. heaptrack: Valgrind runs 20-30x slower and uses significantly more memory. Use it for final verification, not during normal development. For profiling memory allocations in production applications, heaptrack (from KDE) has much lower overhead and can attach to running processes.

FAQ

Is high RSS always a memory leak?

No. RSS includes shared libraries, which can be shared across processes. A process with 500MB RSS might only be using 150MB of PSS. High RSS is a starting point for investigation, not proof of a leak. You need to track RSS over time to confirm a leak pattern.

How do I know if it is a leak or just caching?

Memory leaks show a steady, unbounded increase in RSS or Private_Dirty over hours or days. Caching shows fluctuation: memory usage goes up when you open files and down when the kernel reclaims cache. If your memory usage stabilizes after an initial spike, it is caching. If it never stops growing, it is a leak.

Should I clear my RAM cache regularly?

No. This is one of the most persistent pieces of bad advice on the internet. Clearing your page cache forces the kernel to re-read everything from disk, making your system slower, not faster. I explained why in my guide to clearing RAM cache. Only clear cache if you are benchmarking disk performance or debugging a specific issue.

What about zswap and zram?

These are compressed memory subsystems that help when you are running low on RAM. They compress pages in memory before swapping to disk. They do not fix memory leaks, but they can extend the usable life of memory-constrained systems. I covered them in detail in my RAM optimization guide.

Can I use GNOME System Monitor to track leaks?

Yes, but it is not ideal. GNOME System Monitor shows memory usage in real-time, but it does not log history or export data. For leak detection, you need to track memory over time and look for trends. The ps command with a logging script or sar gives you the historical data you need.

What if the leak is in a kernel module?

Kernel memory leaks are rare on desktop systems but do happen. Symptoms include /proc/meminfo showing high “Slab” or “KernelStack” values that never decrease. Debugging kernel leaks requires tools like slabtop and /proc/slabinfo. This is advanced territory; if you suspect a kernel leak, file a bug report with your distribution.

Conclusion

Memory leaks on Linux desktops are common but diagnosable. The workflow is straightforward: identify the suspect process with top or smem, track its memory over time with a monitoring script, deep-dive its memory map with pmap or smaps, and then fix the culprit based on what you find.

The most important lesson I have learned after years of debugging memory issues is this: do not assume high memory usage is a problem. Linux uses RAM aggressively for caching, and that is a feature, not a bug. The real problem is when memory usage grows without bound and never decreases. That is a leak, and now you have the tools to find it.

For more memory management techniques, see my guides on clearing RAM cache, zswap and zram optimization, and systemd resource limits.

Scroll to Top