Linux Memory Leak Analysis: free -m High "Used" Memory vs Cached/Buffers
Quick answer
High "used" memory reported by free -m is very often not a leak at all — Linux deliberately uses available RAM for disk caching and buffers to improve...
High "used" memory reported by free -m is very often not a leak at all — Linux deliberately uses available RAM for disk caching and buffers to improve performance, and this cached memory is immediately reclaimable whenever an application actually needs it, which is fundamentally different from memory a leaking process has genuinely locked away and made unavailable.
The Problem
Checking memory usage shows what looks like alarmingly high consumption:
free -m
total used free shared buff/cache available
Mem: 16000 14200 300 200 1500 1600
14GB "used" out of 16GB total looks concerning at first glance, especially if no specific application seems to be consuming anywhere near that much memory when checked individually.
Why It Happens
Linux operates on the principle that unused RAM is wasted RAM — rather than leaving free memory genuinely idle, the kernel uses it to cache recently accessed disk data (page cache) and hold filesystem metadata buffers, since this cached data can dramatically speed up subsequent access to the same files without needing a slower disk read. Critically, this cached memory is not locked away the way memory held by an actual leaking process is — it's immediately, automatically released back to the system the moment any application actually needs that RAM for genuine allocation. This means free -m's raw "used" column, taken in isolation, significantly overstates how much memory is actually unavailable for new use.
The Fix — Reading the Output Correctly
Focus on the available column specifically, not the raw used column, when assessing genuine memory pressure — available is a more accurate, kernel-calculated estimate of memory that could actually be provided to a new application right now, accounting for the fact that cache and buffers can be reclaimed instantly:
free -m
total used free shared buff/cache available
Mem: 16000 14200 300 200 1500 1600
In this example, despite "used" showing 14.2GB, available shows 1.6GB genuinely available for new allocations — this is the number that actually matters for assessing whether the system is under real memory pressure, not the raw "used" figure, which includes a large amount of easily-reclaimable cache.
To distinguish genuine, growing process memory usage (a potential leak) from normal cache accumulation, monitor memory usage over time specifically at the process level, looking for a process whose own memory footprint keeps growing without bound, rather than relying on the system-wide "used" figure alone:
watch -n 5 'ps aux --sort=-%mem | head -10'
A genuine memory leak shows a specific process's own RSS (resident set size) memory usage climbing steadily over time, with no plateau, correlating with that process's own ongoing activity — this is a meaningfully different pattern from overall system "used" memory simply reflecting normal, healthy disk cache accumulation as the system has been running and accessing files.
For a more detailed breakdown of exactly what's consuming memory, /proc/meminfo provides finer-grained detail than free's summary view:
cat /proc/meminfo | head -20
Look specifically at Cached, Buffers, and MemAvailable here for the same underlying distinction free presents in summarized form, useful when you want more granular visibility than the standard free command's summary view provides.
Recognizing a Genuine Leak
A genuine memory leak typically shows: a specific process's own memory usage (checked via ps or top, focused on that specific process, not system-wide totals) growing continuously over time without ever plateauing or decreasing, even during periods when that process should logically be idle or between distinct units of work; the system's available memory correspondingly and genuinely decreasing over the same period, not just the raw "used" figure; and, eventually, if left unaddressed, the OOM killer terminating processes once genuinely available memory is exhausted, which is the eventual real-world consequence a true leak produces that normal cache accumulation alone never does, since cache is always reclaimed automatically before that point is ever reached.
Still Not Working?
If you've confirmed available memory is genuinely low and shrinking over time, and a specific process's own memory usage is confirmed to be growing without bound, proceed to application-level memory profiling tools specific to that process's runtime (like Valgrind for C/C++, a heap profiler for Node.js or Python, or your specific language's own memory analysis tooling) to identify exactly what within that specific process is accumulating and never being released — system-level tools like free and ps can confirm that a leak exists and roughly how severe it is, but pinpointing its exact cause within the application's own code requires tools designed specifically for that purpose.