Sandcorp샌드코프 · 웹·AI 제품 개발

새 프로젝트 받는 중 · 갱신 2026.11sandcorp.co.krGitHub

← 팁

메모리디버깅

RSS가 줄지 않을 때는 누수인지 할당자 보유인지 먼저 가른다

타입별 객체 수가 안정적이면 누수가 아니라 할당자가 빈 메모리를 들고 있는 것이다. 객체 수 → mallinfo2 → malloc_trim 순서로 나눠 본다.

파이썬 서버의 RSS가 올라가기만 하고 내려오지 않는다. 누수라고 단정하고 코드를 뒤지기 전에, 정말 객체가 쌓이는 것인지부터 가른다. 객체는 다 풀렸는데 할당자가 빈 메모리를 OS에 돌려주지 않는 경우가 많다.

순서대로 본다.

  1. 타입별 객체 수: gc.get_objects()를 타입별로 세어 시간에 따라 비교한다. 어떤 타입이 계속 늘면 누수다. gc.get_referrers()로 그 객체를 붙잡는 쪽을 찾는다.
  2. 힙의 빈 공간: 객체 수가 안정적이면 mallinfo2()로 힙 크기 대비 빈 공간(fordblks)을 본다. 많이 비어 있으면 할당자 보유다.
  3. 확정: malloc_trim(0)을 한 번 불러 RSS가 내려가는지 본다. 내려가면 확정이다.
  4. 영역 나누기: /proc/<pid>/smaps로 힙·arena·mmap 가운데 어디가 큰지 나눈다.
import collections
import ctypes
import gc

def count_types(top: int = 15):
    return collections.Counter(type(o).__name__ for o in gc.get_objects()).most_common(top)

class MallInfo2(ctypes.Structure):
    _fields_ = [(name, ctypes.c_size_t) for name in (
        "arena", "ordblks", "smblks", "hblks", "hblkhd",
        "usmblks", "fsmblks", "uordblks", "fordblks", "keepcost",
    )]

libc = ctypes.CDLL("libc.so.6")
libc.mallinfo2.restype = MallInfo2
info = libc.mallinfo2()
print(f"heap {info.arena >> 20}MB, free {info.fordblks >> 20}MB")
libc.malloc_trim(0)

주의: 진단 코드 자체가 객체를 만들고 메모리를 바꾼다. 같은 측정을 여러 번 되풀이해 비교하고, 한가한 프로세스에서 기준선을 먼저 잡는다.

문의 →