메모리 관리¶
개요¶
파이썬의 메모리 관리에는 모든 파이썬 객체와 데이터 구조를 포함하는 비공개 힙(private heap)을 수반합니다. 이 비공개 힙의 관리는 파이썬 메모리 관리자에 의해 내부적으로 이루어집니다. 파이썬 메모리 관리자는 공유, 세그먼트 화, 사전 할당 또는 캐싱과 같은 동적 스토리지 관리의 다양한 측면을 처리하는 서로 다른 구성 요소를 가지고 있습니다.
가장 낮은 수준에서, 원시 메모리 할당자는 운영 체제의 메모리 관리자와 상호 작용하여 비공개 힙에 모든 파이썬 관련 데이터를 저장하기에 충분한 공간이 있는지 확인합니다. 원시 메모리 할당자 위에, 여러 개의 객체별 할당자가 같은 힙에서 작동하며 각 객체 형의 특성에 맞는 고유한 메모리 관리 정책을 구현합니다. 예를 들어, 정수는 다른 스토리지 요구 사항과 속도/공간 절충을 의미하므로, 정수 객체는 힙 내에서 문자열, 튜플 또는 딕셔너리와는 다르게 관리됩니다. 따라서 파이썬 메모리 관리자는 일부 작업을 객체별 할당자에게 위임하지만, 후자가 비공개 힙의 경계 내에서 작동하도록 합니다.
파이썬 힙의 관리는 인터프리터 자체에 의해 수행되며, 사용자는 힙 내부의 메모리 블록에 대한 객체 포인터를 규칙적으로 조작하더라도, 사용자가 제어할 수 없다는 것을 이해하는 것이 중요합니다. 파이썬 객체와 기타 내부 버퍼를 위한 힙 공간 할당은 이 설명서에 나열된 파이썬/C API 함수를 통해 파이썬 메모리 관리자의 요청에 따라 수행됩니다.
메모리 손상을 피하고자, 확장 작성자는 C 라이브러리에서 내보낸 함수를 파이썬 객체에 대해 실행하지 않아야 합니다: malloc(), calloc(), realloc() 및 free(). 그렇게 한다면, 서로 다른 알고리즘을 구현하고 다른 힘에 작동하기 때문에, C 할당자와 파이썬 메모리 관리자 간에 혼합 호출이 발생하여 치명적인 결과를 초래합니다. 그러나, 다음 예제와 같이 개별 목적으로 C 라이브러리 할당자를 사용하여 메모리 블록을 안전하게 할당하고 해제할 수 있습니다:
PyObject *res;
char *buf = (char *) malloc(BUFSIZ); /* I/O 용 */
if (buf == NULL)
return PyErr_NoMemory();
...buf 를 사용하는 I/O 연산을 수행합니다...
res = PyBytes_FromString(buf);
free(buf); /* malloc 으로 할당했습니다 */
return res;
이 예에서, I/O 버퍼에 대한 메모리 요청은 C 라이브러리 할당자에 의해 처리됩니다. 파이썬 메모리 관리자는 결과로 반환되는 바이트열 객체의 할당에만 관여합니다.
그러나 대부분의 경우, 파이썬 메모리 관리자가 제어하는 파이썬 힙에서 메모리를 할당할 것을 권장합니다. 예를 들어, 인터프리터가 C로 작성된 새로운 객체 유형으로 확장될 때 이것이 필요합니다. 파이썬 힙을 사용하는 또 다른 이유는 확장 모듈의 메모리 요구 사항을 파이썬 메모리 관리자에게 알리고 싶기 때문입니다. 요청된 메모리가 내부적이고 매우 구체적인 목적으로만 사용되는 경우에도 모든 메모리 요청을 파이썬 메모리 관리자에게 위임하면 인터프리터가 전체 메모리 사용량에 대해 더 정확한 정보를 갖게 됩니다. 결과적으로, 특정 상황에서 파이썬 메모리 관리자가 가비지 컬렉션, 메모리 압축 또는 기타 예방 절차와 같은 적절한 조치를 실행하거나 실행하지 않을 수 있습니다. 이전 예시와 같이 C 라이브러리 할당자를 사용하는 경우, I/O 버퍼를 위해 할당된 메모리가 파이썬 메모리 관리자의 제어 범위를 완전히 벗어나게 됨을 유의하십시오.
더 보기
PYTHONMALLOC 환경 변수를 사용하여 파이썬에서 사용하는 메모리 할당자를 구성할 수 있습니다.
PYTHONMALLOCSTATS 환경 변수는 새로운 pymalloc 객체 아레나(arena)가 만들어질 때마다 그리고 종료 시 pymalloc 메모리 할당자의 통계를 인쇄하는 데 사용될 수 있습니다.
할당자 도메인¶
All allocating functions belong to one of three different “domains” (see also
PyMemAllocatorDomain). These domains represent different allocation
strategies and are optimized for different purposes. The specific details on
how every domain allocates memory or what internal functions each domain calls
is considered an implementation detail, but for debugging purposes a simplified
table can be found at 기본 메모리 할당자.
The APIs used to allocate and free a block of memory must be from the same domain.
For example, PyMem_Free() must be used to free memory allocated using PyMem_Malloc().
세 가지 할당 도메인은 다음과 같습니다.
Raw 도메인: 일반 목적의 메모리 버퍼를 위해 할당하며, 할당이 반드시 시스템 할당자로 가야 하거나 할당자가 attached thread state 없이 작동할 수 있는 경우에 사용됩니다. 이 메모리는 시스템에서 직접 요청됩니다. 자세한 내용은 Raw Memory Interface 를 참조하십시오.
“Mem” 도메인: 파이썬 버퍼 및 일반 목적의 메모리 버퍼를 위해 할당하며, 반드시 attached thread state 가 있는 상태에서 수행되어야 하는 경우에 사용됩니다. 이 메모리는 파이썬 전용 힙(private heap)에서 가져옵니다. 자세한 내용은 Memory Interface 를 참조하십시오.
Object 도메인: 파이썬 객체를 위한 메모리를 할당하는 데 사용됩니다. 이 메모리는 파이썬 전용 힙에서 가져옵니다. 자세한 내용은 Object allocators 를 참조하십시오.
참고
free-threaded 빌드에서는 오직 파이썬 객체만 “object” 도메인을 사용하여 할당해야 하며, 모든 파이썬 객체는 해당 도메인에서 할당되어야 합니다. 이는 이 사항이 권장 사항에 불과했으며 필수 요구 사항이 아니었던 이전 파이썬 버전과는 다릅니다.
예를 들어, 버퍼(파이썬 객체가 아님)는 PyMem_Malloc(), PyMem_RawMalloc() 또는 malloc() 을 사용하여 할당해야 하며, PyObject_Malloc() 을 사용해서는 안 됩니다.
Memory Allocation APIs 를 참조하십시오.
원시 메모리 인터페이스¶
다음의 함수 세트는 시스템 할당자의 래퍼입니다. 이 함수들은 스레드 안전(thread-safe)하므로 thread state 가 attached 될 필요가 없습니다.
기본 원시 메모리 할당자는 다음 함수를 사용합니다: malloc(), calloc(), realloc() 및 free(); 0바이트를 요청할 때 malloc(1)(또는 calloc(1, 1))을 호출합니다.
Added in version 3.4.
-
void *PyMem_RawMalloc(size_t n)¶
- …의 일부 안정 ABI 버전 3.13 이후로.
n 바이트를 할당하고 할당된 메모리를 가리키는 void* 형의 포인터를 반환하거나, 요청이 실패하면
NULL을 반환합니다.0바이트를 요청하면 가능하면
PyMem_RawMalloc(1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다. 메모리는 어떤 식으로든 초기화되지 않습니다.
-
void *PyMem_RawCalloc(size_t nelem, size_t elsize)¶
- …의 일부 안정 ABI 버전 3.13 이후로.
각 크기가 elsize 바이트인 nelem 개의 요소를 할당하고 할당된 메모리를 가리키는 void* 타입의 포인터를 반환하며, 요청이 실패하면
NULL을 반환합니다. 메모리는 0으로 초기화됩니다.0개의 요소나 0바이트 크기의 요소를 요청하면
PyMem_RawCalloc(1, 1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다.Added in version 3.5.
-
void *PyMem_RawRealloc(void *p, size_t n)¶
- …의 일부 안정 ABI 버전 3.13 이후로.
p가 가리키는 메모리 블록의 크기를 n 바이트로 조정합니다. 내용은 이전과 새로운 크기의 최솟값 내에서는 변경되지 않습니다.
p가
NULL이면, 호출은PyMem_RawMalloc(n)과 동등합니다; n이 0과 같으면, 메모리 블록의 크기는 조정되지만 해제되지는 않고, 반환된 포인터는NULL이 아닙니다.p가
NULL이 아닌 한,PyMem_RawMalloc(),PyMem_RawRealloc()또는PyMem_RawCalloc()에 대한 이전 호출에 의해 반환된 것이어야 합니다.요청이 실패하면,
PyMem_RawRealloc()은NULL을 반환하고 p는 이전 메모리 영역에 대한 유효한 포인터로 유지됩니다.
-
void PyMem_RawFree(void *p)¶
- …의 일부 안정 ABI 버전 3.13 이후로.
p가 가리키는 메모리 블록을 해제합니다. p는
PyMem_RawMalloc(),PyMem_RawRealloc()또는PyMem_RawCalloc()에 대한 이전 호출로 반환된 것이어야 합니다. 그렇지 않거나PyMem_RawFree(p)가 앞서 호출되었으면, 정의되지 않은 동작이 일어납니다.p가
NULL이면, 아무 작업도 수행되지 않습니다.
메모리 인터페이스¶
ANSI C 표준에 따라 모델링 되었지만 0바이트를 요청할 때의 동작을 지정한 다음 함수 집합은 파이썬 힙에서 메모리를 할당하고 해제하는 데 사용할 수 있습니다.
GIL이 활성화된 빌드(기본 빌드)에서 default memory allocator 는 pymalloc memory allocator 을 사용하지만, free-threaded build 에서는 대신 mimalloc memory allocator 이 기본값으로 사용됩니다.
경고
이 함수들을 사용할 때는 반드시 attached thread state 가 있어야 합니다.
버전 3.6에서 변경: 기본 할당자는 이제 시스템 malloc() 대신 pymalloc 입니다.
버전 3.13에서 변경: free-threaded 빌드에서 기본 할당자는 이제 mimalloc 입니다.
-
void *PyMem_Malloc(size_t n)¶
- …의 일부 안정 ABI.
n 바이트를 할당하고 할당된 메모리를 가리키는 void* 형의 포인터를 반환하거나, 요청이 실패하면
NULL을 반환합니다.0바이트를 요청하면
PyMem_Malloc(1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다. 메모리는 어떤 식으로든 초기화되지 않습니다.
-
void *PyMem_Calloc(size_t nelem, size_t elsize)¶
- …의 일부 안정 ABI 버전 3.7 이후로.
각 크기가 elsize 바이트인 nelem 개의 요소를 할당하고 할당된 메모리를 가리키는 void* 타입의 포인터를 반환하며, 요청이 실패하면
NULL을 반환합니다. 메모리는 0으로 초기화됩니다.0개의 요소나 0바이트 크기의 요소를 요청하면
PyMem_Calloc(1, 1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다.Added in version 3.5.
-
void *PyMem_Realloc(void *p, size_t n)¶
- …의 일부 안정 ABI.
p가 가리키는 메모리 블록의 크기를 n 바이트로 조정합니다. 내용은 이전과 새로운 크기의 최솟값 내에서는 변경되지 않습니다.
p가
NULL이면, 호출은PyMem_Malloc(n)과 동등합니다; 그렇지 않고 n이 0과 같으면, 메모리 블록의 크기는 조정되지만 해제되지는 않으며, 반환된 포인터는NULL이 아닙니다.p가
NULL이 아닌 한,PyMem_Malloc(),PyMem_Realloc()또는PyMem_Calloc()에 대한 이전 호출이 반환한 것이어야 합니다.요청이 실패하면,
PyMem_Realloc()은NULL을 반환하고 p는 이전 메모리 영역에 대한 유효한 포인터로 유지됩니다.
-
void PyMem_Free(void *p)¶
- …의 일부 안정 ABI.
p가 가리키는 메모리 블록을 해제합니다. p는
PyMem_Malloc(),PyMem_Realloc()또는PyMem_Calloc()에 대한 이전 호출이 반환한 것이어야 합니다. 그렇지 않거나PyMem_Free(p)가 앞서 호출되었으면 정의되지 않은 동작이 일어납니다.p가
NULL이면, 아무 작업도 수행되지 않습니다.
편의를 위해 다음과 같은 형 지향 매크로가 제공됩니다. TYPE이 모든 C형을 나타냄에 유의하십시오.
-
PyMem_New(TYPE, n)¶
PyMem_Malloc()과 같지만,(n * sizeof(TYPE))바이트의 메모리를 할당합니다.TYPE*로 캐스트 된 포인터를 반환합니다. 메모리는 어떤 식으로든 초기화되지 않습니다.
-
PyMem_Resize(p, TYPE, n)¶
PyMem_Realloc()과 같지만, 메모리 블록의 크기는(n * sizeof(TYPE))바이트로 조정됩니다.TYPE*로 캐스트 된 포인터를 반환합니다. 반환한 후에, p는 새로운 메모리 영역에 대한 포인터이거나, 실패하면NULL이 됩니다.이것은 C 전처리기 매크로입니다; p는 항상 다시 대입됩니다. 에러를 처리할 때 메모리 손실을 피하려면 p의 원래 값을 보관하십시오.
-
void PyMem_Del(void *p)¶
PyMem_Free()와 같습니다.
폐지된 에일리어스¶
이들은 기존 함수 및 매크로에 대한 soft deprecated 에일리어스입니다. 오직 하위 호환성을 위해 존재합니다.
폐지된 에일리어스 |
대응하는 함수 또는 매크로 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
버전 3.4에서 변경: 이제 해당 매크로들은 대응하는 함수 및 매크로의 에일리어스입니다. 이전에는 동작 방식이 동일했으나, 이들을 사용하는 것이 파이썬 버전을 넘나드는 바이너리 호환성을 보장하지 않았습니다.
버전 2.0부터 폐지됨.
객체 할당자¶
ANSI C 표준에 따라 모델링 되었지만 0바이트를 요청할 때의 동작을 지정한 다음 함수 집합은 파이썬 힙에서 메모리를 할당하고 해제하는 데 사용할 수 있습니다.
참고
Customize Memory Allocators 섹션에 설명된 방법으로 이 도메인의 할당 함수를 가로챌 때, 이 할당자들이 반환하는 메모리가 파이썬 객체로 성공적으로 캐스팅될 수 있다는 보장이 없습니다.
default object allocator 는 pymalloc memory allocator 을 사용합니다. free-threaded 빌드에서는 대신 mimalloc memory allocator 가 기본값으로 사용됩니다.
경고
이 함수들을 사용할 때는 반드시 attached thread state 가 있어야 합니다.
-
void *PyObject_Malloc(size_t n)¶
- …의 일부 안정 ABI.
n 바이트를 할당하고 할당된 메모리를 가리키는 void* 형의 포인터를 반환하거나, 요청이 실패하면
NULL을 반환합니다.0바이트를 요청하면
PyObject_Malloc(1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다. 메모리는 어떤 식으로든 초기화되지 않습니다.
-
void *PyObject_Calloc(size_t nelem, size_t elsize)¶
- …의 일부 안정 ABI 버전 3.7 이후로.
각 크기가 elsize 바이트인 nelem 개의 요소를 할당하고 할당된 메모리를 가리키는 void* 타입의 포인터를 반환하며, 요청이 실패하면
NULL을 반환합니다. 메모리는 0으로 초기화됩니다.0개의 요소나 0바이트 크기의 요소를 요청하면
PyObject_Calloc(1, 1)이 대신 호출된 것처럼 가능하면 고유한NULL이 아닌 포인터를 반환합니다.Added in version 3.5.
-
void *PyObject_Realloc(void *p, size_t n)¶
- …의 일부 안정 ABI.
p가 가리키는 메모리 블록의 크기를 n 바이트로 조정합니다. 내용은 이전과 새로운 크기의 최솟값 내에서는 변경되지 않습니다.
p가
NULL이면, 호출은PyObject_Malloc(n)과 동등합니다; 그렇지 않고 n이 0과 같으면, 메모리 블록의 크기는 조정되지만 해제되지 않고, 반환된 포인터는NULL이 아닙니다.p가
NULL이 아닌 한,PyObject_Malloc(),PyObject_Realloc()또는PyObject_Calloc()에 대한 이전 호출에 의해 반환된 것이어야 합니다.요청이 실패하면,
PyObject_Realloc()은NULL을 반환하고 p는 이전 메모리 영역에 대한 유효한 포인터로 유지됩니다.
-
void PyObject_Free(void *p)¶
- …의 일부 안정 ABI.
p가 가리키는 메모리 블록을 해제합니다. 이 블록은
PyObject_Malloc(),PyObject_Realloc()또는PyObject_Calloc()에 대한 이전 호출에 의해 반환된 것이어야 합니다. 그렇지 않거나PyObject_Free(p)가 이전에 호출되었으면 정의되지 않은 동작이 일어납니다.p가
NULL이면, 아무 작업도 수행되지 않습니다.객체의 메모리를 해제하기 위해 이 함수를 직접 호출하지 마십시오. 대신 해당 형의
tp_free슬롯을 호출하십시오.PyObject_GC_New또는PyObject_GC_NewVar로 할당된 메모리에 이것을 사용하지 마십시오. 대신PyObject_GC_Del()을 사용하십시오.더 보기
PyObject_GC_Del()은 가비지 컬렉션을 지원하는 타입에 의해 할당된 메모리에 대해 이 함수와 동일한 역할을 합니다.
기본 메모리 할당자¶
기본 메모리 할당자:
구성 |
이름 |
PyMem_RawMalloc |
PyMem_Malloc |
PyObject_Malloc |
|---|---|---|---|---|
릴리스 빌드 |
|
|
|
|
디버그 빌드 |
|
|
|
|
pymalloc 없는 배포 빌드 |
|
|
|
|
pymalloc 없는 디버그 빌드 |
|
|
|
|
free-threaded 빌드 |
|
|
|
|
free-threaded 디버그 빌드 |
|
|
|
|
범례:
이름:
PYTHONMALLOC환경 변수의 값.malloc: 표준 C 라이브러리의 시스템 할당자, C 함수:malloc(),calloc(),realloc()및free().pymalloc: pymalloc 메모리 할당자.mimalloc: mimalloc memory allocator.“+ 디버그”: 파이썬 메모리 할당기의 디버그 훅 포함.
“디버그 빌드”: Python build in debug mode.
메모리 할당자 사용자 정의¶
Added in version 3.4.
-
type PyMemAllocatorEx¶
메모리 블록 할당자를 기술하는 데 사용되는 구조체. 구조체에는 다음과 같은 필드가 있습니다:
필드
의미
void *ctx첫 번째 인자로 전달된 사용자 컨텍스트
void* malloc(void *ctx, size_t size)메모리 블록을 할당합니다
void* calloc(void *ctx, size_t nelem, size_t elsize)0으로 초기화된 메모리 블록을 할당합니다
void* realloc(void *ctx, void *ptr, size_t new_size)메모리 블록을 할당하거나 크기 조정합니다
void free(void *ctx, void *ptr)메모리 블록을 해제합니다
버전 3.5에서 변경:
PyMemAllocator구조체의 이름이PyMemAllocatorEx로 바뀌고 새로운calloc필드가 추가되었습니다.
-
type PyMemAllocatorDomain¶
할당자 도메인을 식별하는 데 사용되는 열거형. 도메인:
-
PYMEM_DOMAIN_RAW¶
함수:
-
PYMEM_DOMAIN_MEM¶
함수:
-
PYMEM_DOMAIN_OBJ¶
함수:
-
PYMEM_DOMAIN_RAW¶
-
void PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
지정된 도메인의 메모리 블록 할당자를 가져옵니다.
-
void PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)¶
지정된 도메인의 메모리 블록 할당자를 설정합니다.
새 할당자는 0바이트를 요청할 때 고유한
NULL이 아닌 포인터를 반환해야 합니다.For the
PYMEM_DOMAIN_RAWdomain, the allocator must be thread-safe: a thread state is not attached when the allocator is called.나머지 도메인의 경우에도 할당자는 스레드 세이프해야 합니다. 할당자가 GIL 을 공유하지 않는 서로 다른 인터프리터에서 호출될 수 있기 때문입니다.
새 할당자가 훅이 아니면 (이전 할당자를 호출하지 않으면),
PyMem_SetupDebugHooks()함수를 호출하여 새 할당자 위에 디버그 훅을 다시 설치해야 합니다.PyPreConfig.allocator및 PyPreConfig로 Python 사전 초기화하기 를 참조하십시오.경고
PyMem_SetAllocator()은 다음과 같은 계약(contract)을 따릅니다:사용자 정의 메모리 할당자를 설치하기 위해
Py_PreInitialize()이후에Py_InitializeFromConfig()이전에 호출할 수 있습니다. 도메인이 부과하는 제약 사항 외에는 설치된 할당자에 대한 제한이 없습니다 (예를 들어, Raw Domain은 attached thread state 없이 할당자가 호출되도록 허용합니다). 자세한 내용은 allocator domains 섹션을 참조하십시오.Python의 초기화가 완료된 후(즉,
Py_InitializeFromConfig()이 호출된 후)에 호출되는 경우, 할당자는 반드시 기존 할당자를 래핑(wrap)해야 합니다. 현재 할당자를 임의의 다른 것으로 교체하는 것은 지원되지 않습니다.
버전 3.12에서 변경: 모든 할당자는 스레드 세이프해야 합니다.
-
void PyMem_SetupDebugHooks(void)¶
메모리 오류를 감지하려면 파이썬 메모리 할당기의 디버그 훅 을 설정하십시오.
파이썬 메모리 할당자의 디버그 훅¶
파이썬을 디버그 모드로 빌드할 때, 메모리 오류를 감지하기 위해 파이썬 메모리 할당기에 디버그 훅을 설정하는 PyMem_SetupDebugHooks() 함수가 파이썬 사전 초기화 단계에서 호출됩니다.
PYTHONMALLOC 환경 변수를 사용하여 릴리스 모드에서 컴파일된 파이썬에 디버그 훅을 설치할 수 있습니다 (예: PYTHONMALLOC=debug).
PyMem_SetAllocator() 을 호출한 후 디버그 훅을 설정하는 데 PyMem_SetupDebugHooks() 함수를 사용할 수 있습니다.
이 디버그 훅은 동적으로 할당된 메모리 블록을 특수하고, 인식 가능한 비트 패턴으로 채웁니다. 새로 할당된 메모리는 바이트 0xCD(PYMEM_CLEANBYTE)로 채워지고, 해제된 메모리는 바이트 0xDD(PYMEM_DEADBYTE)로 채워집니다. 메모리 블록은 바이트 0xFD (PYMEM_FORBIDDENBYTE)로 채워진 “금지된 바이트”로 둘러싸입니다. 이 바이트들의 문자열은 유효한 주소, 부동 소수점 또는 ASCII 문자열일 가능성이 낮습니다.
실행 시간 검사:
API 위반 탐지. 예를 들어,
PyMem_Malloc()이 할당한 메모리 블록에PyObject_Free()가 호출되었는지 탐지합니다.버퍼 시작 전에 쓰기 감지 (버퍼 언더플로).
버퍼 끝 뒤에 쓰기 감지 (버퍼 오버플로).
Check that there is an attached thread state when allocator functions of
PYMEM_DOMAIN_OBJ(ex:PyObject_Malloc()) andPYMEM_DOMAIN_MEM(ex:PyMem_Malloc()) domains are called.
에러가 발생하면, 디버그 훅은 tracemalloc 모듈을 사용하여 메모리 블록이 할당된 곳의 트레이스백을 가져옵니다. tracemalloc이 파이썬 메모리 할당을 추적 중이고 메모리 블록이 추적될 때만 트레이스백이 표시됩니다.
S = sizeof(size_t) 라고 가정합니다. 요청된 N 바이트의 각 블록 양 끝에 2*S 바이트가 추가됩니다. 메모리 레이아웃은 다음과 같으며, 여기서 p는 malloc이나 realloc과 유사한 함수에서 반환된 주소를 나타냅니다(p[i:j] 는 *(p+i) 를 포함하고 *(p+j) 를 포함하지 않는 바이트 슬라이스을 의미합니다. 음수 인덱스의 처리는 파이썬 슬라이스와 다르다는 점에 유의하십시오):
p[-2*S:-S]원래 요청한 바이트 수입니다. 이는 빅 엔디안 방식의 size_t입니다(메모리 덤프에서 읽기 쉽도록 함).
p[-S]API 식별자(ASCII 문자):
PYMEM_DOMAIN_RAW에 대한'r'.PYMEM_DOMAIN_MEM에 대한'm'.PYMEM_DOMAIN_OBJ에 대한'o'.
p[-S+1:0]PYMEM_FORBIDDENBYTE의 복사본입니다. 오버라이트(under-write) 및 읽기를 감지하는 데 사용됩니다.
p[0:N]초기화되지 않은 메모리에 대한 참조를 감지하기 위해 PYMEM_CLEANBYTE 복사본으로 채워진 요청된 메모리입니다. 더 큰 메모리 블록을 요청하는 realloc과 유사한 함수가 호출되면, 새로 추가된 바이트도 PYMEM_CLEANBYTE로 채워집니다. free와 유사한 함수가 호출되면, 이들은 해제된 메모리에 대한 참조를 감지하기 위해 PYEM_DEADBYTE로 덮어씌워집니다. 더 작은 메모리 블록을 요청하는 realloc과 유사한 함수가 호출될 때 남은 기존 바이트도 PYMEM_DEADBYTE로 채워집니다.
p[N:N+S]PYMEM_FORBIDDENBYTE의 복사본입니다. 오버라이트(over-write) 및 읽기를 감지하는 데 사용됩니다.
p[N+S:N+2*S]PYMEM_DEBUG_SERIALNO매크로가 정의된 경우에만 사용됩니다(기본적으로 정의되지 않음).malloc 또는 realloc과 유사한 함수를 호출할 때마다 1씩 증가하는 일련번호입니다. 빅 엔디안 방식의
size_t입니다. 나중에 “잘못된 메모리”가 감지되면, 이 번호를 사용하여 다음 실행 시 해당 블록이 배포된 순간에 중단점(breakpoint)을 설정할 수 있는 훌륭한 수단이 됩니다. obmalloc.c의 정적 함수 bumpserialno()는 일련번호가 증가하는 유일한 위치이며, 이를 통해 쉽게 중단점을 설정할 수 있습니다.
realloc 또는 free와 유사한 함수는 먼저 양 끝의 PYMEM_FORBIDDENBYTE 바이트가 온전한지 확인합니다. 만약 변형되었다면, stderr에 진단 메시지가 출력되고 Py_FatalError()를 통해 프로그램이 종료됩니다. 다른 주요 실패 모드는 프로그램이 특수한 비트 패턴 중 하나를 읽고 이를 주소로 사용하려고 할 때 메모리 오류를 유발하는 것입니다. 이때 디버거에서 해당 객체를 확인하면, 전체가 PYMEM_DEADBYTE(해제된 메모리 사용 중임을 의미) 또는 PYMEM_CLEANBYTE(초기화되지 않은 메모리 사용 중임을 의미)로 채워져 있는 것을 볼 가능성이 높습니다.
버전 3.6에서 변경: The PyMem_SetupDebugHooks() function now also works on Python
compiled in release mode. On error, the debug hooks now use
tracemalloc to get the traceback where a memory block was allocated.
The debug hooks now also check if there is an attached thread state when
functions of PYMEM_DOMAIN_OBJ and PYMEM_DOMAIN_MEM domains are
called.
버전 3.8에서 변경: 바이트 패턴 0xCB(PYMEM_CLEANBYTE), 0xDB(PYMEM_DEADBYTE) 및 0xFB(PYMEM_FORBIDDENBYTE)는 윈도우 CRT 디버그 malloc() 및 free()와 같은 값을 사용하도록 0xCD, 0xDD 및 0xFD로 대체되었습니다.
pymalloc 할당자¶
파이썬은 수명이 짧고 크기가 작은(512바이트 이하) 객체에 최적화된 pymalloc 할당기를 갖추고 있습니다. 이 할당기는 32비트 플랫폼에서 256 KiB, 64비트 플랫폼에서 1 MiB의 고정 크기를 가진 “아레나(arenas)”라는 메모리 매핑을 사용합니다. 파이썬을 --with-pymalloc-hugepages 옵션과 함께 구성하면, 64비트 플랫폼에서 아레나 크기가 huge page 크기에 맞춰 2 MiB로 증가하며, 아레나 할당 시 huge page를 사용하도록 시도하고 실패할 경우 일반 페이지로 자동 전환합니다. 512바이트보다 큰 할당의 경우 PyMem_RawMalloc() 및 PyMem_RawRealloc() 으로 대체됩니다.
pymalloc은 PYMEM_DOMAIN_MEM(예: PyMem_Malloc())과 PYMEM_DOMAIN_OBJ(예: PyObject_Malloc()) 도메인의 기본 할당자입니다.
아레나 할당자는 다음 함수를 사용합니다:
윈도우에서
VirtualAlloc()과VirtualFree(),사용할 수 있으면
mmap()과munmap()그렇지 않으면
malloc()과free()
파이썬을 --without-pymalloc 옵션과 함께 구성하면 이 할당기가 비활성화됩니다. 또한 실행 시점에 PYTHONMALLOC 환경 변수를 사용하여 비활성화할 수 있습니다(예: PYTHONMALLOC=malloc).
일반적으로 C 코드 내의 낮은 수준의 버그를 찾아내는 데 도움이 되는 AddressSanitizer(--with-address-sanitizer)를 사용하여 파이썬을 빌드할 때는 pymalloc 할당기를 비활성화하는 것이 좋습니다.
pymalloc 아레나 할당자 사용자 정의¶
Added in version 3.4.
-
type PyObjectArenaAllocator¶
아레나 할당자를 기술하는 데 사용되는 구조체. 이 구조체에는 세 개의 필드가 있습니다:
필드
의미
void *ctx첫 번째 인자로 전달된 사용자 컨텍스트
void* alloc(void *ctx, size_t size)size 바이트의 아레나를 할당합니다
void free(void *ctx, void *ptr, size_t size)아레나를 해제합니다
-
void PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)¶
아레나 할당자를 얻습니다.
-
void PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)¶
아레나 할당자를 설정합니다.
mimalloc 할당자¶
Added in version 3.13.
파이썬은 기본 플랫폼에서 지원될 경우 mimalloc 할당기를 지원합니다. mimalloc은 뛰어난 성능 특성을 가진 범용 할당기로, 처음에 Daan Leijen에 의해 Koka 및 Lean 언어의 런타임 시스템을 위해 개발되었습니다.
pymalloc 이 작은 객체(512바이트 이하)에 최적화된 것과 달리, mimalloc은 모든 크기의 할당을 처리합니다.
free-threaded 빌드에서 mimalloc은 PYMEM_DOMAIN_MEM 및 PYMEM_DOMAIN_OBJ 도메인의 기본이며 필수 인 할당자입니다. free-threaded 빌드에서는 이를 비활성화할 수 없습니다. free-threaded 빌드는 스레드별 mimalloc 힙을 사용하여 대부분의 경우 락(locking) 없이 할당 및 해제가 진행될 수 있도록 합니다.
기본(non-free-threaded) 빌드에서 mimalloc은 사용 가능하지만 기본 할당자는 아닙니다. 실행 시점에 PYTHONMALLOC =mimalloc (또는 디버그 훅 을 포함하려면 mimalloc_debug)을 사용하여 선택할 수 있습니다. 빌드 시에 --without-mimalloc 설정 옵션을 사용하여 비활성화할 수 있으나, 이 옵션은 --disable-gil 과 함께 사용할 수 없습니다.
tracemalloc C API¶
Added in version 3.7.
-
int PyTraceMalloc_Track(unsigned int domain, uintptr_t ptr, size_t size)¶
tracemalloc모듈에서 할당된 메모리 블록을 추적합니다.성공하면
0을 반환하고, 에러가 발생하면 (추적을 저장하기 위한 메모리를 할당하지 못했습니다)-1을 반환합니다. tracemalloc이 비활성화되었으면-2를 반환합니다.메모리 블록이 이미 추적되면, 기존 추적을 갱신합니다.
-
int PyTraceMalloc_Untrack(unsigned int domain, uintptr_t ptr)¶
tracemalloc모듈에서 할당된 메모리 블록을 추적 해제합니다. 블록이 추적되지 않으면 아무것도 하지 않습니다.tracemalloc이 비활성화되었으면
-2를 반환하고, 그렇지 않으면0을 반환합니다.
예¶
다음은 개요 섹션에서 따온 예제입니다. I/O 버퍼가 첫 번째 함수 집합을 사용하여 파이썬 힙에서 할당되도록 다시 작성되었습니다:
PyObject *res;
char *buf = (char *) PyMem_Malloc(BUFSIZ); /* I/O 용 */
if (buf == NULL)
return PyErr_NoMemory();
/* ...buf 를 사용하는 I/O 연산을 수행합니다... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* PyMem_Malloc 으로 할당했습니다 */
return res;
형 지향 함수 집합을 사용하는 같은 코드입니다:
PyObject *res;
char *buf = PyMem_New(char, BUFSIZ); /* I/O용 */
if (buf == NULL)
return PyErr_NoMemory();
/* ...buf를 포함하는 일부 I/O 작업 수행... */
res = PyBytes_FromString(buf);
PyMem_Free(buf); /* PyMem_New로 할당됨 */
return res;
위의 두 가지 예에서, 버퍼는 항상 같은 집합에 속하는 함수를 통해 조작됨에 유의하십시오. 실제로, 서로 다른 할당자를 혼합할 위험이 최소로 줄어들도록, 주어진 메모리 블록에 대해 같은 메모리 API 패밀리를 사용하는 것은 필수입니다 . 다음 코드 시퀀스에는 두 개의 에러가 있으며, 그중 하나는 서로 다른 힙에서 작동하는 두 개의 다른 할당자를 혼합하기 때문에 치명적(fatal)인 것으로 표시됩니다.
char *buf1 = PyMem_New(char, BUFSIZ);
char *buf2 = (char *) malloc(BUFSIZ);
char *buf3 = (char *) PyMem_Malloc(BUFSIZ);
...
PyMem_Del(buf3); /* Wrong -- should be PyMem_Free() */
free(buf2); /* Right -- allocated via malloc() */
free(buf1); /* Fatal -- should be PyMem_Free() */
파이썬 힙에서 오는 원시 메모리 블록을 처리하는 것을 목표로 하는 함수 외에도, 파이썬의 객체는 PyObject_New, PyObject_NewVar 및 PyObject_Free() 를 사용하여 할당되고 해제됩니다.
이것들은 C로 새로운 객체 형을 정의하고 구현하는 것에 대한 다음 장에서 설명될 것입니다.