Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 454 – Python 메모리 할당을 추적하는 새 tracemalloc 모듈 추가

Author:
Victor Stinner <vstinner at python.org>
BDFL-Delegate:
Charles-François Natali <cf.natali at gmail.com>
Status:
Final
Type:
Standards Track
Created:
03-Sep-2013
Python-Version:
3.4
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

이 PEP는 Python이 할당한 메모리 블록을 추적하는 새 tracemalloc 모듈을 추가할 것을 제안합니다.

근거

Valgrind 같은 전통적인 범용 도구는 메모리 블록이 할당된 위치의 C 트레이스백을 얻을 수 있습니다. 그러나 이러한 도구로 Python 메모리 할당을 분석해도 도움이 되지 않습니다. 대부분의 메모리 블록이 같은 C 함수, 예를 들어 PyMem_Malloc()에서 할당되기 때문입니다. 게다가 Python에는 “pymalloc”이라는 작은 객체용 할당자가 있는데, 이 할당자는 효율성을 위해 빈 블록을 유지합니다. 이러한 도구는 이것을 잘 처리하지 못합니다.

Heapy, Pympler, Meliae 같은 Python 언어 전용 디버그 도구가 있습니다. 이 도구들은 가비지 컬렉터 모듈(gc.get_objects(), gc.get_referrers(), gc.get_referents() 같은 함수)을 사용하여 살아 있는 모든 객체를 나열하고, (예: sys.getsizeof()를 사용하여) 그 크기를 계산하며, 객체를 타입별로 묶습니다. 이러한 도구는 애플리케이션의 메모리 사용량을 더 잘 추정해 줍니다. 이 도구들은 대부분의 메모리 누수가 같은 타입의 인스턴스이고, 그 타입이 몇몇 함수에서만 인스턴스화될 때 유용합니다. 객체 타입이 str이나 tuple처럼 매우 흔한 경우에는 문제가 생기며, 이러한 객체가 어디에서 인스턴스화되는지 알아내기 어렵습니다.

참조 순환을 찾는 것 또한 어려운 문제입니다. 모든 참조의 다이어그램을 그려 주는 여러 도구가 있습니다. 그러나 이러한 도구는 수천 개의 객체가 있는 대규모 애플리케이션에는 사용할 수 없습니다. 다이어그램이 너무 커서 수작업으로 분석할 수 없기 때문입니다.

제안

PEP 445의 사용자 정의 할당 API를 사용하면 Python 메모리 할당자에 훅을 쉽게 설치할 수 있습니다. 훅은 Python 내부를 검사하여 Python 트레이스백을 가져올 수 있습니다. 현재 트레이스백을 가져온다는 아이디어는 faulthandler 모듈에서 비롯되었습니다. faulthandler는 크래시가 발생하면 모든 Python 스레드의 트레이스백을 덤프합니다. 여기서의 아이디어는 Python이 메모리 블록을 할당할 때 현재 Python 스레드의 트레이스백을 가져오는 것입니다.

이 PEP는 Python이 할당한 메모리 블록을 추적하는 디버그 도구인 새 tracemalloc 모듈을 추가할 것을 제안합니다. 이 모듈은 다음 정보를 제공합니다:

  • 객체가 할당된 위치의 트레이스백
  • 파일 이름별, 줄 번호별로 할당된 메모리 블록에 대한 통계: 할당된 메모리 블록의 총 크기, 개수, 평균 크기
  • 메모리 누수를 감지하기 위해 계산한 두 스냅숏 간의 차이

tracemalloc 모듈의 API는 faulthandler 모듈의 API와 비슷합니다: enable() / start(), disable() / stop(), is_enabled() / is_tracing() 함수, 환경 변수(PYTHONFAULTHANDLER, PYTHONTRACEMALLOC), 그리고 -X 명령줄 옵션(-X faulthandler, -X tracemalloc)이 있습니다. faulthandler 모듈 문서를 참조하십시오.

메모리 할당을 추적한다는 아이디어는 새로운 것이 아닙니다. 이 아이디어는 2005년 PySizer 프로젝트에서 처음 구현되었습니다. PySizer는 다른 방식으로 구현되었습니다: 트레이스백은 프레임 객체에 저장되었고, 일부 Python 타입은 추적 정보를 객체 타입의 이름과 연결했습니다. CPython에 적용한 PySizer 패치는 PySizer를 사용하지 않을 때에도 성능과 메모리 사용량에 오버헤드를 더합니다. tracemalloc은 트레이스백을 하위 계층인 메모리 블록에 붙이며, 모듈이 메모리 할당을 추적하지 않을 때에는 오버헤드가 없습니다.

tracemalloc 모듈은 CPython용으로 작성되었습니다. 다른 Python 구현에서는 이 모듈을 제공하지 못할 수도 있습니다.

API

파이썬이 할당하는 메모리 블록을 최대한 많이 추적하려면 PYTHONTRACEMALLOC 환경 변수를 1로 설정하거나 -X tracemalloc 명령줄 옵션을 사용하여 모듈을 가능한 한 일찍 시작해야 합니다. tracemalloc.start() 함수는 런타임에 호출하여 Python 메모리 할당 추적을 시작할 수 있습니다.

기본적으로 할당된 메모리 블록의 트레이스에는 가장 최근 프레임 하나(1 프레임)만 저장됩니다. 시작할 때 프레임 25개를 저장하려면 PYTHONTRACEMALLOC 환경 변수를 25로 설정하거나 -X tracemalloc=25 명령줄 옵션을 사용하십시오. set_traceback_limit() 함수는 런타임에 제한을 설정하는 데 사용할 수 있습니다.

함수

clear_traces() 함수:

Python이 할당한 메모리 블록의 트레이스를 지웁니다.

stop()도 참고하십시오.

get_object_traceback(obj) 함수:

Python 객체 obj가 할당된 위치의 트레이스백을 가져옵니다. Traceback 인스턴스를 반환하며, tracemalloc 모듈이 메모리 할당을 추적하고 있지 않거나 해당 객체의 할당을 추적하지 않았다면 None을 반환합니다.

gc.get_referrers() 및 sys.getsizeof() 함수도 참고하십시오.

get_traceback_limit() 함수:

트레이스의 트레이스백에 저장되는 최대 프레임 수를 가져옵니다.

한도를 가져오려면 tracemalloc 모듈이 메모리 할당을 추적하고 있어야 하며, 그렇지 않으면 예외가 발생합니다.

한도는 start() 함수로 설정합니다.

get_traced_memory() 함수:

tracemalloc 모듈이 추적하는 메모리 블록의 현재 크기와 최대 크기를 (size: int, max_size: int) 튜플로 가져옵니다.

get_tracemalloc_memory() 함수:

tracemalloc 모듈이 메모리 블록의 트레이스를 저장하는 데 사용하는 메모리 사용량을 바이트 단위로 가져옵니다. int를 반환합니다.

is_tracing() 함수:

tracemalloc 모듈이 Python 메모리 할당을 추적하고 있으면 True, 그렇지 않으면 False입니다.

start() 및 stop() 함수도 참고하십시오.

start(nframe: int=1) 함수:

Python 메모리 할당 추적을 시작합니다: Python 메모리 할당자에 훅을 설치합니다. 수집되는 트레이스의 트레이스백은 nframe 프레임으로 제한됩니다. 기본적으로 메모리 블록의 트레이스에는 가장 최근 프레임만 저장됩니다. 즉, 한도는 1입니다. nframe은 1 이상이어야 합니다.

1개보다 많은 프레임을 저장하는 것은 'traceback'으로 그룹화된 통계나 누적 통계를 계산할 때만 유용합니다. Snapshot.compare_to()와 Snapshot.statistics() 메서드를 참조하십시오.

더 많은 프레임을 저장하면 tracemalloc 모듈의 메모리 및 CPU 오버헤드가 늘어납니다. tracemalloc 모듈이 사용하는 메모리양을 측정하려면 get_tracemalloc_memory() 함수를 사용하십시오.

PYTHONTRACEMALLOC 환경 변수(PYTHONTRACEMALLOC=NFRAME)와 -X tracemalloc=NFRAME 명령줄 옵션을 사용하면 시작할 때 바로 추적을 시작할 수 있습니다.

stop(), is_tracing(), get_traceback_limit() 함수도 참조하십시오.

stop() 함수:

Python 메모리 할당 추적을 중지합니다. Python 메모리 할당자에 설치된 훅을 제거합니다. 또한 Python이 할당한 메모리 블록의 트레이스도 지웁니다.

트레이스를 지우기 전에 스냅숏을 찍으려면 take_snapshot() 함수를 호출하십시오.

start()와 is_tracing() 함수도 참조하십시오.

take_snapshot() 함수:

Python이 할당한 메모리 블록의 트레이스 스냅숏을 찍습니다. 새 Snapshot 인스턴스를 반환합니다.

tracemalloc 모듈이 메모리 할당을 추적하기 시작하기 전에 할당된 메모리 블록은 스냅숏에 포함되지 않습니다.

트레이스의 트레이스백은 get_traceback_limit()개의 프레임으로 제한됩니다. 더 많은 프레임을 저장하려면 start() 함수의 nframe 매개변수를 사용하십시오.

스냅숏을 찍으려면 tracemalloc 모듈이 메모리 할당을 추적하고 있어야 합니다. start() 함수를 참조하십시오.

get_object_traceback() 함수도 참조하십시오.

Filter

Filter(inclusive: bool, filename_pattern: str, lineno: int=None, all_frames: bool=False) 클래스:

메모리 블록의 트레이스를 필터링합니다.

filename_pattern의 문법은 fnmatch.fnmatch() 함수를 참조하십시오. '.pyc'와 '.pyo' 파일 확장자는 '.py'로 대체됩니다.

예시:

  • Filter(True, subprocess.__file__)는 subprocess 모듈의 트레이스만 포함합니다
  • Filter(False, tracemalloc.__file__)는 tracemalloc 모듈의 트레이스를 제외합니다
  • Filter(False, "<unknown>")는 빈 트레이스백을 제외합니다

inclusive 속성:

inclusive가 True(포함)이면, 이름이 filename_pattern과 일치하는 파일의 lineno번 줄에서 할당된 메모리 블록만 추적합니다.

inclusive가 False(제외)이면, 이름이 filename_pattern과 일치하는 파일의 lineno번 줄에서 할당된 메모리 블록을 무시합니다.

lineno 속성:

필터의 줄 번호(int)입니다. lineno가 None이면 필터는 모든 줄 번호와 일치합니다.

filename_pattern 속성:

필터의 파일 이름 패턴(str)입니다.

all_frames 속성:

all_frames가 True이면 트레이스백의 모든 프레임을 검사합니다. all_frames가 False이면 가장 최근 프레임만 검사합니다.

트레이스백 제한이 2보다 작으면 이 속성은 무시됩니다. get_traceback_limit() 함수와 Snapshot.traceback_limit 속성을 참조하십시오.

Frame

Frame 클래스:

트레이스백의 프레임입니다.

Traceback 클래스는 Frame 인스턴스의 시퀀스입니다.

filename 속성:

파일 이름(str)입니다.

lineno 속성:

줄 번호(int)입니다.

스냅숏

Snapshot 클래스:

Python이 할당한 메모리 블록의 트레이스 스냅숏입니다.

take_snapshot() 함수는 스냅숏 인스턴스를 생성합니다.

compare_to(old_snapshot: Snapshot, group_by: str, cumulative: bool=False) 메서드:

이전 스냅숏과의 차이를 계산합니다. group_by로 그룹화한 StatisticDiff 인스턴스의 정렬된 리스트로 통계를 가져옵니다.

group_by 및 cumulative 매개변수에 대해서는 statistics() 메서드를 참조하십시오.

결과는 StatisticDiff.size_diff의 절댓값, StatisticDiff.size, StatisticDiff.count_diff의 절댓값, Statistic.count, 그리고 마지막으로 StatisticDiff.traceback을 기준으로 가장 큰 것부터 가장 작은 것 순으로 정렬됩니다.

dump(filename) 메서드:

스냅숏을 파일에 기록합니다.

스냅숏을 다시 불러오려면 load()를 사용하십시오.

filter_traces(filters) 메서드:

필터링된 traces 시퀀스를 가진 새 Snapshot 인스턴스를 생성합니다. filters는 Filter 인스턴스의 리스트입니다. filters가 빈 리스트이면 트레이스의 사본을 가진 새 Snapshot 인스턴스를 반환합니다.

모든 포함 필터는 한 번에 적용되며, 어떤 포함 필터와도 일치하지 않는 트레이스는 무시됩니다. 배제 필터가 하나라도 일치하는 트레이스는 무시됩니다.

load(filename) 클래스 메서드:

파일에서 스냅숏을 불러옵니다.

dump()도 참조하십시오.

statistics(group_by: str, cumulative: bool=False) 메서드:

group_by에 따라 그룹화된 통계를 Statistic 인스턴스의 정렬된 리스트로 가져옵니다:
group_by 설명
'filename' 파일명
'lineno' 파일명과 줄 번호
'traceback' 트레이스백

cumulative가 True이면, 가장 최근 프레임뿐만 아니라 트레이스의 트레이스백에 있는 모든 프레임의 메모리 블록 크기와 개수를 누적합니다. 누적 모드는 group_by가 'filename' 또는 'lineno'이고 traceback_limit이 1보다 클 때만 사용할 수 있습니다.

결과는 Statistic.size, Statistic.count, 그다음 Statistic.traceback 순서의 기준에 따라 큰 것부터 작은 것 순으로 정렬됩니다.

traceback_limit 속성:

traces의 트레이스백에 저장되는 최대 프레임 수: 스냅숏을 생성했을 때의 get_traceback_limit() 결과입니다.

traces 속성:

Python이 할당한 모든 메모리 블록의 트레이스: Trace 인스턴스의 시퀀스입니다.

시퀀스의 순서는 정의되어 있지 않습니다. 정렬된 통계 리스트를 얻으려면 Snapshot.statistics() 메서드를 사용하십시오.

Statistic

Statistic 클래스:

메모리 할당에 대한 통계입니다.

Snapshot.statistics()는 Statistic 인스턴스의 리스트를 반환합니다.

StatisticDiff 클래스도 참조하십시오.

count 속성:

메모리 블록 수(int)입니다.

size 속성:

메모리 블록의 전체 크기(바이트 단위, int)입니다.

traceback 속성:

메모리 블록이 할당된 위치의 트레이스백(Traceback 인스턴스)입니다.

StatisticDiff

StatisticDiff 클래스:

이전 Snapshot 인스턴스와 새 Snapshot 인스턴스 간의 메모리 할당 통계 차이입니다.

Snapshot.compare_to()는 StatisticDiff 인스턴스의 리스트를 반환합니다. Statistic 클래스도 참조하십시오.

count 속성:

새 스냅숏의 메모리 블록 수(int)이며, 새 스냅숏에서 메모리 블록이 해제되었으면 0입니다.

count_diff 속성:

이전 스냅숏과 새 스냅숏 간의 메모리 블록 수 차이(int)이며, 새 스냅숏에서 메모리 블록이 할당되었으면 0입니다.

size 속성:

새 스냅숏의 메모리 블록 전체 크기(바이트 단위, int)이며, 새 스냅숏에서 메모리 블록이 해제되었으면 0입니다.

size_diff 속성:

이전 스냅숏과 새 스냅숏 간의 메모리 블록 전체 크기 차이(바이트 단위, int)이며, 새 스냅숏에서 메모리 블록이 할당되었으면 0입니다.

traceback 속성:

메모리 블록들이 할당된 위치의 트레이스백(Traceback 인스턴스)입니다.

Trace

Trace 클래스:

메모리 블록의 트레이스입니다.

Snapshot.traces 속성은 Trace 인스턴스의 시퀀스입니다.

size 속성:

메모리 블록의 크기(바이트 단위, int)입니다.

traceback 속성:

메모리 블록이 할당된 위치의 트레이스백(Traceback 인스턴스)입니다.

Traceback

Traceback 클래스:

가장 최근 프레임부터 가장 오래된 프레임 순으로 정렬된 Frame 인스턴스의 시퀀스입니다.

트레이스백에는 최소 1개의 프레임이 포함됩니다. tracemalloc 모듈이 프레임을 가져오지 못하면 줄 번호 0의 파일 이름 "<unknown>"이 사용됩니다.

스냅숏을 찍을 때 트레이스의 트레이스백은 get_traceback_limit() 프레임으로 제한됩니다. take_snapshot() 함수를 참조하십시오.

Trace.traceback 속성은 Traceback 인스턴스입니다.

기각된 대안

메모리 할당자 호출 기록하기

다른 접근 방식은 malloc(), realloc(), free() 함수 호출을 기록하는 것입니다. 호출은 파일에 기록하거나 네트워크를 통해 다른 컴퓨터로 전송할 수 있습니다. 로그 항목의 예: 함수 이름, 메모리 블록의 크기, 메모리 블록의 주소, 할당이 발생한 위치의 Python 트레이스백, 타임스탬프.

로그는 직접 사용할 수 없으며, 메모리의 현재 상태를 얻으려면 이전 로그를 파싱해야 합니다. 예를 들어, 트레이스를 사용하는 get_object_traceback(obj)처럼 Python 객체의 트레이스백을 직접 얻는 것은 불가능합니다.

Python은 수명이 매우 짧은 객체를 사용하므로 메모리 할당자를 광범위하게 사용합니다. Python에는 수명이 짧은 작은 객체(512바이트 미만)에 최적화된 할당자가 있습니다. 예를 들어, Python 테스트 스위트는 malloc(), realloc() 또는 free()를 평균적으로 초당 270,000번 호출합니다. 로그 항목의 크기가 32바이트라면, 로깅은 초당 8.2MB, 즉 시간당 29.0GB를 생성합니다.

이 대안은 효율이 떨어지고 기능도 더 적기 때문에 거부되었습니다. 다른 프로세스나 다른 컴퓨터에서 로그를 파싱하는 것은 같은 프로세스에서 할당된 메모리 블록의 트레이스를 유지하는 것보다 느립니다.

선행 작업

  • Python Memory Validator (2005-2013): Software Verification이 개발한 상용 Python 메모리 검증 도구입니다. Python Reflection API를 사용합니다.
  • PySizer: Nick Smallbone의 Google Summer of Code 2005 프로젝트입니다.
  • Heapy (2006-2013): Sverker Nilsson이 작성한 Guppy-PE 프로젝트의 일부입니다.
  • PEP 초안: Support Tracking Low-Level Memory Usage in CPython (Brett Canon, 2006)
  • Muppy: Robert Schuppenies가 2008년에 개발한 프로젝트입니다.
  • asizeof: Jean Brouwers가 작성한, 객체의 크기를 추정하는 순수 Python 모듈입니다(2008).
  • Heapmonitor: 개별 객체의 크기를 측정하는 기능을 제공하며, 특정 클래스의 모든 객체를 추적할 수 있습니다. Ludwig Haehne가 2008년에 개발했습니다.
  • Pympler (2008-2011): asizeof, muppy, HeapMonitor를 기반으로 한 프로젝트입니다.
  • objgraph (2008-2012)
  • Dozer: Marius Gedminas가 작성한 CherryPy 메모리 누수 디버거의 WSGI 미들웨어 버전입니다(2008-2013).
  • Meliae: John A Meinel이 2009년부터 개발해 온 Python 메모리 사용량 분석기입니다.
  • gdb-heap: 힙 메모리 사용량을 분석하기 위해 Dave Malcolm이 Python으로 작성한 gdb 스크립트입니다(2010-2011).
  • memory_profiler: Fabian Pedregosa가 작성했습니다(2011-2013).
  • caulk: 2012년 Ben Timby가 작성

Pympler Related Work도 참고하십시오.

링크

tracemalloc: