One of the most common GNU/Linux topics that users ask about is memory use. There are many guides, but most of the guides written by experienced systems engineers focus on performance, and it’s hard to find a comprehensive guide to the simple question, “what is using memory in this system?”

The standard tools: free and top

The most common process accounting tools are provided by procps. These will be available by default on nearly all Linux systems.

free will give you a quick summary of the system, overall. I’ll use the -h option here (but sometimes it’s easier to spot outliers when numbers aren’t converted to human readable units):

$ free -h
               total        used        free      shared  buff/cache   available
Mem:            15Gi       9.0Gi       2.8Gi       3.5Gi       6.5Gi       6.4Gi
Swap:          8.0Gi       2.8Gi       5.2Gi

In this example, the host has approximately 16 GiB of RAM, and is configured for 8 GiB of swap on zram.

“Available” is a value provided by the Linux kernel which estimates the amount of memory that could be given to applications without causing the kernel to start searching for memory pages to reclaim or swap, and the “used” value is the “total” minus “available” memory.

A relatively small portion of the page cache (where Linux caches files) will present as “used” memory. In some cases, especially if you have a large amount of memory and a lot of it is currently used for page cache, the amount of memory that appears to be “used” might be larger than you expect. I’ll discuss that later.

top can also give you a summary of the state of the system, but adds detail about individual processes.

Tasks: 379 total, 1 running, 378 sleep, 0 d-sleep, 0 stopped, 0 zombie
%Cpu(s):  1.3 us,  0.8 sy,  0.0 ni, 97.3 id,  0.0 wa,  0.3 hi,  0.3 si,  0.0 st
MiB Mem :  15687.4 total,   2534.7 free,   9103.6 used,   6748.9 buff/cache
MiB Swap:   8192.0 total,   5370.1 free,   2821.9 used.   6583.7 avail Mem

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   5814 gordon    20   0   13.2g   1.1g 307780 S   2.1   7.0 214:53.65 firefox
 605677 gordon    20   0 3431312 665044 130088 S   0.0   4.1   0:43.69 Isolated Web Co
   6144 gordon    20   0 3780996 649408 117208 S   0.0   4.0  18:30.94 WebExtensions
   4998 gordon    20   0 4438168 608468 194156 S   0.0   3.8  16:09.63 thunderbird

Here we see information about memory use that’s similar to the information provided by free, but we also see a description of how much time CPUs spend in various states and information about specific processes. The resident size (RES) of a process provides information about how much memory a program is using, but it can be misleading, because memory can be shared among multiple processes.

top can also display information about how much of a process’ memory is in swap, but this is not in the default set of columns. If you want to identify processes using swap space, press the f key to open the field manager, use the arrow keys to select the SWAP field, then press the d key (and s if you want to sort based on that field), and finally q to return to the process list.

Making sense of processes sharing memory: smem

The smem tool is not part of procps and users will probably need to install it. In addition to the information that the standard tools can provide, smem adds a “proportional” size field that represents the memory that is unique to one process plus a fraction of its shared memory, divided among the processes sharing that memory segment.

Adding the resident size of several processes may over-estimate their memory use when they share memory segments, but adding their “proportional” size will not.

$ smem -s pss -r | head
PID     User    Command                      Swap    USS     PSS     RSS
5814    gordon  /usr/lib64/firefox/firefox   203036  839880  932231  1174208
6144    gordon  /usr/lib64/firefox/firefox   69852   566560  571748  688328
605677  gordon  /usr/lib64/firefox/firefox   3868    530256  537617  664648
4998    gordon  /usr/lib64/thunderbird/thun  184692  462264  510504  621840
561266  gordon  /usr/lib64/firefox/firefox   126240  398612  406060  531920
606913  gordon  /usr/lib64/firefox/firefox   3796    324420  331144  456280
5985    gordon  /usr/lib64/firefox/firefox   13864   322400  330093  449216
571137  gordon  /usr/lib64/firefox/firefox   20856   295132  301865  426320
23559   gordon  /usr/lib64/firefox/firefox   110424  275128  280471  405600

Examining kernel memory use: slabtop

slabtop is another tool provided by procps-ng. It provides a sorted list, like top, but this list represents different data types allocated in the kernel. Run sudo slabtop --human, and press the c key to sort by the size of each cache.

 Active / Total Objects (% used)    : 2267240 / 2936377 (77.2%)
 Active / Total Slabs (% used)      : 59549 / 59549 (100.0%)
 Active / Total Caches (% used)     : 438 / 535 (81.9%)
 Active / Total Size (% used)       : 386Mi / 502Mi (77.0%)
 Minimum / Average / Maximum Object : 0.01K / 0.17K / 12.50K

    OBJS   ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME
   88118    66583  75%    0.93K   2717       34       84Mi btrfs_inode
  149191    97228  65%    0.57K   5347       28       83Mi radix_tree_node
    1975     1925  97%   12.50K    988        2       30Mi task_struct
  146076    97308  66%    0.19K   6956       21       27Mi dentry

In this example, we can see that 84 MiB have been allocated to store btrfs inodes, and 27 MiB have been allocated to store directory entries.

When these values are small, users can usually ignore the specific meanings of each data type. But if something is very large, users can look up how that type is used and whether the size of that cache should be investigated further.

Disk and filesystem cache

Disk and network storage is very slow, relative to a CPU and RAM. Memory that isn’t used for anything more important will be used by the kernel to improve the efficiency of reading and writing to slow storage. The vast majority of this memory is just a copy of information that’s on those devices, so when an application requests a memory allocation, the kernel can simply drop some of that cached memory.

But remember that the “available” memory value is an estimate. The kernel will start searching for pages to swap out before it drops 100% of the filesystem cache. Some of this memory affects the “used” value.

The process of calculating the amount of page cache that is treated as “used” memory is complex, as seen here. You can use the used-page-cache script to estimate how much of your system’s “used” memory is actually cache which the kernel may be able to free without necessarily pushing pages into swap.

You can also tune the kernel to protect less memory from reclamation. This will generally make performance worse later if you use enough memory to cause the system to start swapping, but if you’re trying to observe the amount of memory that is being used for something other than cache, you can do it anyway.

echo 8448 | sudo tee /proc/sys/vm/min_free_kbytes

Furthermore, you can instruct the kernel to immediately drop most cache memory, which may make it easier to get a simple value that represents the amount of memory used overall for things other than cache.

$ grep SReclaim /proc/meminfo ; echo 3 | sudo tee /proc/sys/vm/drop_caches ; grep SReclaim /proc/meminfo
SReclaimable:     272620 kB
3
SReclaimable:      67088 kB

Files stored in RAM

Many Linux systems will use “tmpfs” to create directories that store files in RAM. If you copy files to those directories, free will report more memory used. Use df to examine the amount of space “used” by tmpfs filesystems.

$ df -t tmpfs
Filesystem     1K-blocks  Used Available Use% Mounted on
tmpfs            8031932    92   8031840   1% /dev/shm
tmpfs            3212776  3044   3209732   1% /run
none                1024     0      1024   0% /run/credentials/systemd-journald.service
none                1024     0      1024   0% /run/credentials/systemd-resolved.service
tmpfs            8031932  3924   8028008   1% /tmp
tmpfs            1606384   368   1606016   1% /run/user/556600005

ZRAM

zram is a kernel feature that creates a storage device in RAM. Some systems use this device instead of swap in order to compress memory without swapping to disk. On a system that swaps to disk, memory in swap is not “used”, but on a system that uses swap on zram, the compressed data is still “used” RAM.

Use zramctl to get information about memory used by zram devices.

$ zramctl
NAME       ALGORITHM DISKSIZE  DATA  COMPR TOTAL STREAMS MOUNTPOINT
/dev/zram0 lzo-rle         8G  2.4G 707.4M  725M         [SWAP]

ZFS

ZFS is a filesystem developed by the OpenZFS project. It is not part of the standard Linux kernel because it uses the CDDL license, which is not compatible with the GPL 2.0 license.

One of the unusual behaviors of ZFS is that its filesystem cache (ARC) appears to be “used” memory in the output of free rather than in the buff/cache column like other filesystems.

Use the arcstat tool to get information about memory “used” by ZFS:

$ arcstat
    time  read  ddread  ddh%  dmread  dmh%  pread  ph%   size      c  avail
11:58:00     9       0     0       9   100      0    0   125G   126G  52.3G

HugePages

The Linux kernel supports a feature named “HugePages” that allows processes to reserve large blocks of memory, as well as allocating blocks larger than the standard 4k page size.

When memory is reserved for HugePages, it will appear as “used” memory, even when the software that reserved the memory isn’t running.

If you can’t find memory used anywhere else, look for memory that might be reserved for HugePages:

$ grep Huge /proc/meminfo
AnonHugePages:         0 kB
ShmemHugePages:   352256 kB
FileHugePages:         0 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
Hugetlb:               0 kB

References:

1: https://kernel-internals.org/mm/understanding-proc-meminfo/ 2: https://blog.thalheim.io/2025/10/17/zfs-ate-my-ram-understanding-the-arc-cache/