본문 바로가기
Operating System

리눅스 메모리가 꽉 찬 것처럼 보일 때 - free의 available, buff/cache, swap 제대로 읽기

강철지그·2026년 8월 29일·조회 1

모니터링 알림에 "메모리 사용률 92%"가 뜨면 반사적으로 재시작 버튼에 손이 간다. 그런데 막상 서버에 들어가 free -h를 쳐보면 아무 문제 없이 잘 돌고 있는 경우가 대부분이다. 리눅스는 남는 메모리를 캐시로 채워두기 때문에, usedfree 숫자만 보고 부족 여부를 판단하면 거의 항상 틀린다. 컨테이너 환경까지 오면 이 오해가 더 심해진다.

결론부터 말하자면, 리눅스에서 "쓸 수 있는 메모리"는 freefree 칸이 아니라 available 칸이다. buff/cache는 대부분 필요할 때 회수되는 캐시라 여유로 봐야 하고, 컨테이너의 실제 사용량은 호스트 free가 아니라 cgroup v2의 memory.current에서 캐시를 뺀 값으로 봐야 한다. 아래에서 각각을 명령과 출력으로 짚어본다.

먼저 알아둘 용어: page cache와 buff/cache

리눅스 커널은 디스크에서 읽은 파일 내용을 램에 캐시로 남겨둔다. 이걸 page cache라고 부른다. 같은 파일을 다시 읽을 때 디스크까지 안 가도 되니 빠르다. 이 캐시는 프로세스가 메모리를 요구하면 커널이 알아서 버리고 내준다. 즉 캐시로 잡힌 메모리는 사실상 여유 메모리다.

freebuff/cache 칸은 이 page cache에 버퍼(Buffers, 블록 디바이스용 임시 저장)와 tmpfs(Shmem) 등을 합친 값이다. 이 숫자가 크다고 겁먹을 필요가 없다. 오히려 램을 놀리지 않고 잘 쓰고 있다는 뜻이다.

free -h 읽는 순서

$ free -h
               total        used        free      shared  buff/cache   available
Mem:            15Gi       4.2Gi       512Mi       128Mi        11Gi        11Gi
Swap:          2.0Gi       256Mi       1.8Gi

여기서 사람들이 free 512Mi를 보고 "곧 죽겠다"고 판단한다. 그건 틀렸다. procps의 man 페이지는 각 칸을 이렇게 정의한다.

  • free: 어디에도 안 쓰이는 순수 미사용 메모리(MemFree). 캐시로도 안 쓰이는 상태라 오히려 낭비에 가깝다.
  • buff/cache: 버퍼와 page cache의 합. 압박이 오면 커널이 회수한다.
  • available: 스왑 없이 새 프로그램을 띄우는 데 쓸 수 있는 메모리의 추정치.
  • used: total - available로 계산한다. 최신 procps는 used도 available 기준으로 뽑는다.

위 출력이라면 available가 11Gi다. 전체 15Gi 중 11Gi를 새 작업에 쓸 수 있으니 여유롭다. used 4.2Gi도 total에서 available를 뺀 값이라, 실제 프로세스가 쥐고 있는 애플리케이션 메모리에 가깝다. 판단은 available 한 칸만 보면 된다.

available가 free보다 믿을 만한 이유

available는 단순히 "미사용 + 캐시"를 더한 게 아니다. 커널 문서에 따르면 MemAvailableMemFree, 회수 가능한 슬랩(SReclaimable), 파일 LRU 리스트 크기, 그리고 각 존의 low watermark를 종합해 추정한다. 시스템이 정상 동작하려면 어느 정도 page cache가 남아야 한다는 점, 그리고 슬랩이 전부 회수되지는 않는다는 점까지 감안한 값이다.

그래서 "미사용 + buff/cache = 여유"라고 손으로 더하는 계산보다 available가 현실적이다. 커널이 이미 회수 불가능한 몫을 빼줬기 때문이다.

top의 헤더도 같은 논리로 읽는다

MiB Mem :  15869.0 total,    512.3 free,   4321.7 used,  11035.0 buff/cache
MiB Swap:   2048.0 total,   1792.0 free,    256.0 used.  11298.5 avail Mem

top 두 번째 줄 끝의 avail Memfreeavailable와 같은 값이다. 마침표 뒤에 붙어 있어 놓치기 쉬운데, 실제 여유를 보려면 이 avail Mem을 봐야 한다. buff/cache 11GB는 여기서도 회수 가능한 캐시다.

/proc/meminfo로 근거를 직접 확인한다

freetop은 모두 /proc/meminfo를 가공한 결과다. 의심스러우면 원본을 본다.

$ grep -E 'MemTotal|MemFree|MemAvailable|Buffers|^Cached|SReclaimable|SwapTotal|SwapFree|Shmem' /proc/meminfo
MemTotal:       16249876 kB
MemFree:          524512 kB
MemAvailable:   11298432 kB
Buffers:          182340 kB
Cached:         10520132 kB
SReclaimable:     412300 kB
SwapTotal:       2097148 kB
SwapFree:        1835008 kB
Shmem:            131072 kB

몇 가지 함정이 여기 숨어 있다.

  • Cached에는 tmpfs와 공유 메모리(Shmem)가 포함된다. tmpfs에 올린 파일은 디스크 백업이 없어 그냥 회수되지 않는다. 그래서 Cached가 크다고 전부 회수 가능한 것은 아니다. 커널이 available를 계산할 때 이런 몫을 이미 감안한다.
  • SReclaimable은 슬랩 중 회수 가능한 캐시(예: dentry, inode 캐시)다. freebuff/cache에는 이 값도 더해진다.

swap은 used가 아니라 si/so로 판단한다

swap은 램이 부족할 때 디스크로 밀어내는 예비 공간이다. free에서 Swapused가 256Mi라고 해서 지금 성능 문제가 있다는 뜻은 아니다. 과거 한가할 때 밀려난 페이지가 그대로 남아 있는 경우가 많다. 중요한 건 지금 스왑이 오가고 있느냐이고, 그건 vmstatsi(swap-in), so(swap-out) 열로 본다.

$ vmstat 1 3
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0 262144 524512 182340 10520132   0    0    12    45  320  610  4  1 95  0  0
 1  0 262144 521300 182340 10521100   0    0     0     8  298  580  3  1 96  0  0
 0  0 262144 519880 182344 10521800   0    0     0    16  305  592  3  1 96  0  0

siso가 계속 0이면 스왑에 데이터가 좀 들어 있어도 실시간 스와핑은 없다. 문제는 이 두 값이 매 초 수백~수천 kB씩 꾸준히 찍힐 때다. 그때는 램이 실제로 모자라 디스크와 페이지를 주고받는 중이고, 응답 지연으로 이어진다. wa(iowait)까지 같이 올라가면 거의 확실하다.

컨테이너에서 호스트 free는 거짓말을 한다

컨테이너 안에서 free -h를 치면 컨테이너에 걸어둔 메모리 제한이 아니라 호스트 전체 메모리가 나온다. free/proc/meminfo를 읽는데, 이 파일은 컨테이너의 cgroup 제한을 반영하지 않기 때문이다. 8GB 호스트에서 512MB로 제한한 컨테이너인데도 free는 8GB를 여유처럼 보여준다. 이 상태로 애플리케이션이 힙을 키우면 컨테이너만 OOM으로 죽는다.

컨테이너의 진짜 사용량은 cgroup v2 인터페이스 파일에서 읽는다. cgroup은 프로세스 묶음의 자원 사용을 제어하는 커널 기능이고, v2에서는 /sys/fs/cgroup 아래 파일로 값을 노출한다.

$ cat /sys/fs/cgroup/memory.current
268435456
$ cat /sys/fs/cgroup/memory.max
536870912
$ grep -E '^anon |^file |^inactive_file ' /sys/fs/cgroup/memory.stat
anon 157286400
file 94371840
inactive_file 62914560

커널 문서 기준으로 각 파일의 뜻은 이렇다.

  • memory.current: 이 cgroup과 그 하위가 현재 쓰는 메모리 총량(바이트).
  • memory.max: 이 cgroup의 메모리 상한. 여기에 도달하고 더 못 줄이면 cgroup 안에서 OOM 킬러가 동작한다.
  • memory.stat의 anon: 익명 매핑 메모리. 힙, 스택처럼 파일 백업이 없어 회수하려면 스왑이 필요하다.
  • memory.stat의 file: 파일 캐시로 쓰는 메모리(tmpfs, 공유 메모리 포함).
  • memory.stat의 inactive_file: 파일 캐시 중 비활성 LRU에 있는, 회수 우선순위가 높은 몫.

여기서 핵심은 memory.current에 파일 캐시(file)가 포함된다는 점이다. 위 예에서 memory.current는 256MB지만, 그중 상당 부분이 회수 가능한 파일 캐시다. 제한(memory.max) 512MB에 가까워 보여도 압박이 오면 캐시부터 버려진다.

working set: 컨테이너의 회수 불가 실사용

그래서 "이 컨테이너가 진짜로 쥐고 있는, 회수 못 하는 메모리"를 보려면 캐시를 빼야 한다. 쿠버네티스 kubelet과 cAdvisor가 쓰는 방식이 이 계산이다.

working_set = memory.current - inactive_file
            = 268435456 - 62914560
            ≈ 196 MiB

OOM 판단과 kubectl top pod이 보여주는 값이 이 working set 계열이다. memory.current가 제한에 근접해도 working set이 넉넉하면 대개 캐시가 부풀린 것이고, working set 자체가 제한에 붙으면 그때가 진짜 위험 신호다. 값은 환경에 따라 다르지만, 판단 기준은 memory.current 단독이 아니라 inactive_file을 뺀 값이라는 점이 중요하다.

정리

메모리 여유를 판단할 때 보는 순서는 하나로 굳혀두면 편하다. 호스트에서는 free -havailable(또는 topavail Mem) 한 칸을 보고, buff/cache는 여유로 취급한다. 스왑은 used가 아니라 vmstatsi/so로 실시간 스와핑 여부를 확인한다. 컨테이너에서는 free를 믿지 말고 memory.current에서 inactive_file을 뺀 working set을 memory.max와 비교한다. 이 세 가지만 지켜도 멀쩡한 서버를 습관적으로 재시작하는 일은 사라진다.

자주 묻는 질문

free에서 free 값이 매우 작은데 서버가 곧 죽나요?

아닙니다. free 칸은 캐시로도 안 쓰이는 순수 미사용 메모리라 작은 게 정상입니다. 실제 여유는 available 칸으로 판단하세요. 리눅스는 남는 램을 page cache로 채우고, 프로세스가 요구하면 그 캐시를 회수해 내줍니다.

buff/cache가 전체 램의 70%를 차지하는데 줄여야 하나요?

줄일 필요 없습니다. buff/cache는 대부분 회수 가능한 파일 캐시라 여유로 봐야 합니다. 램을 놀리지 않고 잘 쓰고 있다는 신호입니다. 다만 tmpfs(Shmem)에 올린 데이터는 회수되지 않으므로 Cached 전부가 회수 가능한 것은 아니며, 이 몫은 available 계산에 이미 반영됩니다.

swap used가 계속 남아 있는데 성능 문제인가요?

swap의 used 값 자체는 문제가 아닙니다. 과거에 밀려난 페이지가 남아 있을 뿐일 수 있습니다. 지금 스와핑이 일어나는지는 vmstat의 si(swap-in)와 so(swap-out) 열로 보세요. 두 값이 매 초 꾸준히 찍히면 램 부족이고, 계속 0이면 문제 없습니다.

컨테이너 안에서 free -h를 치면 왜 호스트 전체 메모리가 나오나요?

free는 /proc/meminfo를 읽는데 이 파일은 컨테이너의 cgroup 메모리 제한을 반영하지 않기 때문입니다. 컨테이너 실사용량은 /sys/fs/cgroup/memory.current, 제한은 memory.max에서 읽어야 정확합니다.

memory.current가 제한에 가까운데 위험한가요?

memory.current에는 회수 가능한 파일 캐시가 포함되므로 단독으로는 판단하기 어렵습니다. memory.stat의 inactive_file을 뺀 working set 값을 memory.max와 비교하세요. working set이 제한에 붙으면 그때가 실제 OOM 위험 신호입니다.

관련 글

댓글 0

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.