
파이썬 애플리케이션을 운영하다 보면 sys.getsizeof()로 측정한 객체 크기의 총합보다 실제 프로세스가 점유하고 있는 RSS(Resident Set Size) 메모리가 훨씬 큰 경우를 자주 목격하게 됩니다. 개발자는 "왜 데이터 크기는 100MB인데 프로세스는 500MB를 쓰고 있을까?"라는 의문에 빠지기 쉽습니다. 본 가이드에서는 파이썬의 메모리 관리 아키텍처를 심층 분석하여 측정값과 실제 점유율 사이의 차이가 발생하는 근본 원인을 밝히고, 이를 정확하게 측정하는 방법을 제시합니다.
1. sys.getsizeof()의 한계와 측정 방식의 이해
sys.getsizeof()는 파이썬 객체 자체의 크기(바이트 단위)를 반환하는 함수입니다. 하지만 이 함수는 '얕은 측정(Shallow measurement)'을 수행합니다. 즉, 객체가 직접 들고 있는 메모리 크기만 계산할 뿐, 해당 객체가 참조하고 있는 하위 객체들의 크기는 포함하지 않습니다.
2. 측정값과 실제 점유율 차이의 3가지 핵심 원인
단순 측정값과 실제 OS 보고서상의 메모리 사용량이 다른 이유는 크게 다음과 같은 내부 메커니즘 때문입니다.
| 구분 | 주요 원인 | 상세 설명 |
|---|---|---|
| 1. 참조 구조 | Shallow Copy 특성 | 컨테이너(list, dict)는 내부 요소의 주소값(8바이트)만 크기에 포함함 |
| 2. 메모리 풀링 | PyMalloc (Arena) | 작은 객체를 위해 OS로부터 미리 큰 메모리 블록을 할당받아 관리함 |
| 3. 오버헤드 | Object Header | 모든 객체는 Ref Count와 Type Pointer 등 관리를 위한 추가 메모리가 필요함 |
| 4. 가비지 컬렉션 | Fragmentation | 객체 삭제 후에도 OS에 메모리를 즉시 반환하지 않아 파편화 발생 |
3. 컨테이너 객체의 메모리 측정 함정
리스트(List)나 딕셔너리(Dict)를 측정할 때 가장 큰 오해가 발생합니다. 예를 들어, 1,000개의 큰 문자열을 담은 리스트의 sys.getsizeof() 값은 리스트 구조 자체의 크기와 1,000개의 포인터 크기일 뿐입니다. 실제 문자열 데이터가 차지하는 공간은 이 측정값에 전혀 반영되지 않습니다.
4. 해결 방법: 재귀적 측정과 도구 활용
단순한 수치를 넘어 실제 점유율을 파악하기 위해서는 객체 그래프를 순회하며 모든 하위 객체의 크기를 합산하거나, 외부 프로파일링 도구를 사용해야 합니다.
Sample Example: 재귀적 객체 크기 측정 함수
아래 코드는 객체가 참조하는 모든 하위 요소까지 포함하여 실제 메모리 사용량을 추정하는 방법을 보여줍니다.
import sys
def get_full_size(obj, seen=None):
"""객체의 하위 참조까지 포함한 실제 크기를 재귀적으로 계산"""
size = sys.getsizeof(obj)
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
if isinstance(obj, dict):
size += sum([get_full_size(v, seen) for v in obj.values()])
size += sum([get_full_size(k, seen) for k in obj.keys()])
elif hasattr(obj, '__dict__'):
size += get_full_size(obj.__dict__, seen)
elif hasattr(obj, '__iter__') and not isinstance(obj, (str, bytes, bytearray)):
size += sum([get_full_size(i, seen) for i in obj])
return size
# 테스트
test_list = ["data" * 100 for _ in range(1000)]
print(f"Shallow Size: {sys.getsizeof(test_list)} bytes")
print(f"Deep Size: {get_full_size(test_list)} bytes")
5. OS 레벨에서의 실제 메모리 확인 (RSS vs VMS)
파이썬 코드 내부에서의 측정과 별개로, 실제 프로세스가 점유한 메모리를 확인하려면 psutil 라이브러리를 활용하는 것이 좋습니다. RSS(Resident Set Size)는 프로세스가 실제로 RAM에 할당받은 물리적 메모리 크기를 나타내며, 이것이 우리가 흔히 말하는 '실제 메모리 사용량'입니다.
- PyMalloc의 영향: 파이썬은 512바이트 이하의 작은 객체들을 'Arena'라는 256KB 블록 단위로 관리합니다. 객체가 해제되어도 Arena 전체가 비워지지 않으면 OS로 메모리가 반환되지 않아 측정값과 차이가 발생합니다.
- 해결책: 대규모 데이터를 다룰 때는
numpy나pandas처럼 메모리 효율적인 라이브러리를 사용하거나, 슬롯(__slots__)을 사용하여 객체 헤더 오버헤드를 줄여야 합니다.
6. 결론
sys.getsizeof()는 객체 설계 시 개별 요소의 오버헤드를 확인하는 용도로는 훌륭하지만, 시스템 전체의 메모리 가용성을 판단하기에는 부족합니다. 객체 간의 참조 구조, 파이썬 고유의 메모리 풀링 전략, 그리고 OS의 가상 메모리 관리 방식을 종합적으로 고려해야만 정확한 리소스 관리가 가능합니다. 메모리 누수가 의심될 때는 objgraph나 tracemalloc 같은 전문 프로파일러를 병행 사용하시길 권장합니다.
글 내용의 출처 및 참고 자료
- Python Standard Library: sys.getsizeof() documentation
- CPython Source Code:
Objects/obmalloc.c(Memory allocator implementation) - Real Python: Memory Management in Python
- PyCon: The Memory Management of Python Explored by Nina Zakharenko