https://www.honeybadger.io/blog/memory-management-in-python/

 

Memory Management in Python

Understanding memory management is a superpower that will help you design memory-efficient applications and make it easier to debug memory issues. Join Rupesh Mishra for a deep dive into the internals of CPython.

www.honeybadger.io

이 글을 번역한 글입니다.

목차

1. Memory Management in Python (1) - Python 메모리 할당
2. Memory Management in Python (2) - Python 가비지 컬렉터

 


메모리 관리는 컴퓨터 메모리(RAM)를 효율적으로 관리하기 위한 과정으로, 프로그램 요청 시 런타임에 메모리 조각을 프로그램을 할당하고 프로그램이 더 이상 메모리를 필요로 하지 않을 때 할당했던 메모리를 재사용하기 위해 할당된 메모리를 해제하는 작업을 포함한다.

 

C 또는 Rust 의 경우 프로그램에서 메모리를 사용하기 전에 수동으로 메모리를 할당하고 프로그램이 더 이상 필요하지 않을 때 메모리를 해제해야 한다. 즉, 메모리 관리는 프로그래머의 책임이다. Python 에서는 자동으로 메모리 할당 및 할당 해제를 처리한다.

 

이 글에서는 Python 의 메모리 관리 내부적인 동작에 관해 설명한다. 또한, 객체와 같은 기본 단위가 메모리에 저장되는 방법, Python 의 다양한 메모리 할당자 유형, Python 의 메모리 관리자가 메모리를 효율적으로 관리하는 방법에 대해 설명한다.

 

Python 메모리 관리의 내부 동작을 이해하면, 메모리 효율적인 애플리케이션을 설게하는 데 도움이 될것이다. 또한 애플리케이션의 메모리 문제를 더 쉽게 디버깅할 수 있다.

 

Pythond 의 언어 사양에 대해 이해하는 것부터 시작하여 CPython 에 대해 자세히 살펴보자! 

 

Python as a Language Spectification

Python 은 프로그래밍 언어로, 이 문서는 Python 언어에 대한 규칙 및 사양을 명시한 Python 참조 문서이다.

 

예를 들어, Python 언어 사양에 따르면 함수를 정의하기 위해서는 def 키워드를 사용해야 한다고 명시되어 있다.

이는 사양일 뿐이며 컴퓨터가 def function_name 을 사용하면 => function_name 이라는 이름의 함수를 정의하려고 한다는 것을 컴퓨터가 이해할 수 있도록 규칙과 사양에 따라 프로그램을 작성해야 한다.

 

Python 의 언어 규칙 및 사양은 C, Java, 그리고 C# 과 같은 다양한 프로그래밍 언어로 구현된다.

C로 Python 언어를 구현한 것을 CPython 이라고 하고, Java 와 C# 으로 Python 언어를 구현한 것을 각각 Jython 과 IronPython 이라고 한다.

 

What is CPython?

CPython 은 Python 언어의 기본이자 가장 널리 사용되는 구현이다. Python 이라고 하면 대부분 CPython을 의미한다.

python.org 에서 Python 을 다운로드 하면 기본적으로 CPython 코드가 다운로드 된다. 따라서 CPython 은 C 언어로 작성된 프로그램으로, Python 언어에 정의된 모든 규칙과 사양을 구현한다.

CPython 은 Python 프로그래밍 언어의 참조구현이다.
CPython 은 Python 코드를 바이트 코드로 컴파일 후 한 줄씩 실행하기 때문에,
인터프리터와 컴파일러 둘 다로 정의될 수 있다.
- Wikipedia

CPython 은 참조 구현이므로 Python 언어의 모든 새로운 규칙과 사양은 먼저 CPython 을 통해 구현된다.이 글에서는 CPython 의 메모리 관리 내부에 대해 설명한다.참고로 JPython 및 IronPython 과 같은 다른 구현에서는 메모리 관리는 다른 방식으로 할 수도 있다. CPython 은 C 프로그래밍 언어로 구현되었으므로 먼저 C 의 메모리 관리와 관련된 두가지 중요한 기능: malloc 과 free 에 대해 알아보자.

 

What are malloc and free Functions in C?

malloc 은 런타임에 운영 체제에서 메모리 블록을 요청하기 위해 C 프로그래밍 언어에서 사용되는 방법니다. 프로그램이 런타임에 메모리가 필요할 때, 필요한 메모리를 얻기 위해 malloc 메서드를 호출한다.

 

free 는 프로그램이 더 이상 메모리를 필요로 하지 않을 때, 프로그램에 할당된 메모리를 다시 운영체제로 해제하기 위해 C 프로그래밍 언어에서 사용되는 방법이다.

 

Python 프로그램에 메모리가 필요한 경우는 CPython 이 내부적으로 malloc 메서드를 호출하여 메모리를 할당한다. 만약 프로그램에 더 이상 메모리가 필요하지 않으면 CPython 은 free 메서드를 호출하여 메모리를 해제한다.

 

이제 파이썬에서 서로 다른 객체에 메모리가 어떻게 할당되는지 살펴보자!

 

Objects in Python

Python 에서는 모든것이 객체이다. 클래스, 함수, 그리고 integer, floats, string 과 같은 간단한 데이터 유형도 객체이다.

예를 들어, Python 에서 정수를 정의하면 CPython은 내부적으로 정수 유형의 객체를 생성한다. 이러한 개체는 힙 메모리에 저장된다.

 

각 객체는 value, type, reference count 세 필드를 가진다. 

그림1

a = 100

위 코드가 실행되면, CPython 은 정수 유형의 객체를 생성하고 이 객체에 대한 메모리를 힙 메모리에 할당한다.

type 은 CPython 에서 객체의 타입을 나타내며, value 필드는 이름에서 알 수 있듯이 객체의 값(100) 을 저장한다.

 

이 글 뒷부분에서 ref count 필드에 관해 더 자세히 설명할 것이다.

 

 

 

Variables in Python

Python 의 변수는 메모리의 실제 객체에 대한 참조일 뿐이다. 변수들은 메모리에 있는 실제 객체를 가리키는 이름이나 레이블이며, 어떤 값도 저장하지는 않는다. 위 그림과 같이 변수 a 를 사용하여 Python 프로그램에서 정수 객체에 접근할 수 있다.

 

a = 100
b = a

그렇다면 이 정수 객체를 다른 변수 b 에 할당해보자

 

 

 

 

 

 

 

 

 

 

 

그리고 정수 객체의 값을 1 증가해보면,

 

# Increment a by 1
a = a + 1

위 코드가 실행되면 CPython은 값이 101 인 새 정수 객체를 만들고 변수를 이 새 정수 객체를 가리키도록 한다. 변수 b는 100인 정수 개체를 계속 가리킨다.

여기서 우리는 100 의 값을 101 로 덮어쓰지 않고 CPython 이 값 101 의 새 객체를 생성하는 것을 확인할 수 있다.

즉, 일단 생성되면 수정할 수 없으며, 부동 소수점 및 문자열 데이터 유형 또한 Python 에서 변경할 수 없다.

 

 

 

 

 

 

 

 

 

 

이 개념을 더 자세히 설명하기 위해 간단한 Python 프로그램을 살펴보자

# 변수 i 값이 100 미만이 될 때까지 증가시키는 while 루프

i = 0

while i < 100:
"""
# 변수 i 가 증가할 때마다 CPython은 증가된 값으로 새로운 정수 객체를 생성하고
# 이전 정수 객체는 메모리에서 삭제 대상이 된다.
"""
    i = i + 1

CPython 은 이와 같이 각각의 새 객체에 대해 malloc 메서드를 호출하여 해당 객체에 대한 메모리를 할당하고, free 메서드를 호ㅗ출하여 메모리에서 이전 객체를 삭제한다.

 

위의 코드를 malloc 과 free 를 사용한 코드로 변환해보면 아래와 같다.

i = 0  # malloc(i)

while i < 100:
    # malloc(i + 1)
    # free(i)
    i = i + 1

우리는 CPython 이 이렇게 간단한 프로그램에도 많은 수의 객체를 생성하고 삭제하는 것을 확인할 수 있다.

각 객체 생성 및 삭제에 대해 매번 malloc 과 free 메서드를 호출하면 프로그램의 실행 성능이 저하되고 프로그램이 느려진다.

 

따라서 CPython 은 각각의 작은 객체 생성 및 삭제에 대해 malloc 및 free 호출 횟수를 줄이기 위해 다양한 기술을 도입한다.

이제 CPython 이 메모리를 관리하는 방법에 대해 이해해보자

 

Memory Management in CPython

Python 의 메모리 관리에는 프라이빗 힙 관리가 포함된다. 프라이빗 힙은 Python 프로세스의 전용 메모리 영역이다.

모든 Python 객체 및 데이터 구조는 프라이빗 힙에 저장된다.

 

운영체제는 이 메모리 영역을 다른 프로세스에 할당할 수 없으며, 프라이빗 힙의크기는 Python 프로세스의 메모리 요구 사항에 따라 늘어나고 줄어들 수 있다. 프라이빗 힙은 CPython 코드 내부에 정의된 Python 메모리 관리자에 의해 관리된다.

 

CPython 의 프라이빗 힙은 아래 그림과 같이 여러 부분으로 나뉜다.

이러한 각 부분의 경계는 고정되어 있지 않으며, 요구 사항에 따라 확장되거나 축소될 수 있다.

  1. Python Core Non-object memory: Python Core Non-Object 데이터에 할당된 메모리 부분이다.
  2. Internal Buffers: 내부 버퍼에 할당된 메모리 부분이다.
  3. Object-specific memory: 객체별 메모리 할당자가 있는 객체에 할당된 메모리 부분이다.
  4. Object memory: 객체에 할당된 메모리 부분이다.

 

 

 

 

 

 

프로그램이 메모리를 요청하면, CPython 은 malloc 메서드를 사용하여 운영체제에서 해당 메모리를 요청하고, 프라이빗 힙의 크기가 커진다. 그리고 CPython에서는 각각의 작은 객체 생성 및 삭제를 위한 malloc과 free를 호출하지 않도록 다양한 할당자와 해제자를 정의한다. 다음 섹션에서 각각 상세히 알아보자!

 

Memory Allocators

malloc 및 free 메서드를 자주 호출하지 않기 위해 CPython은 아래와 같이 할당자의 계층 구조를 정의한다.

그림의 메모리 계층 구조의 아래에서부터,

  • General Purpose Allocator (CPython 의 malloc 메서드): 메모리 계층 구조의 가장 아래에는 General Purpose Allocator 가 있다. 범용 할당자는 CPython 용 `C 언어의 malloc` 메서드이다. 이는 운영 체제의 가상 메모리 관리자와 상호작용하여 Python 프로세스에 필요한 메모리를 할당한다. 또한, 운영체제의 가상 메모리 관리자와 통신하는 유일한 할당자이다. 
  • Raw memory Allocator (512 byte 보다 큰 객체용): General Purpose Allocator 위에는 Python 의  Raw memory Allocator 가 있다. Raw memory Allocator 는 General Purpose Allocator(=malloc) 에 대한 추상화를 제공한다. Python 프로세스에 메모리가 필요한 경우, Raw memory Allocator 는 General Purpose Allocator 와 상호작용하여 필요한 메모리를 제공한다. 그리고 Python 프로세스의 모든 데이터를 저장할 충분한 메모리 공간이 있는지 확인한다. 
  • Object Allocator (512 byte 보다 작거나 같은 객체용): Raw memory Allocator 위에는 Object Allocator 가 있는데, 이 할당자는 512 byte 이하의 작은 객체에 대한 메모리 할당에 사용된다. 만약 객체에 512 byte 이상의 메모리가 필요한 경우 Python 의 메모리 관리자는 Raw memory Allocator 를 직접 호출한다.
  • Object-specific Allocators (특정 데이터 유형에 대한 특정 메모리 할당자): Object Allocator 위에는 Object-specific Allocators 가 있다. 정수, 실수, 문자열 및 리스트와 같은 단순 데이터 유형에는 각각의 객체별 할당자가 있다. 이러한 객체별 할당자는 객체의 요구 사항에 따라 메모리 관리 정책을 구현한다. 즉, 정수에 대한 객체별 할당자는 실수에 대한 객체별 할당자와 구현 방식이 다르다.
    • Object Allocator 와 Object-specific Allocators 모두 Raw memory Allocator 에 의해 Python 프로세스에 이미 할당된 메모리에서 작동한다. 이러한 할당자는 운영 체제에서 메모리를 직접 요청하지 않고, 프라이빗 힙에서 작동한다. Object Allocator 와 Object-specific Allocators 에 더 많은 메모리가 필요한 경우 Python 의 Raw memory Allocator 가 General Purpose Allocator 와 상호작용하여 메모리를 제공한다.

 

Hierarchy of Memory Allocators in Python

객체가 메모리를 요청하고 객체에 object-specific allocator 가 정의되어 있으면, object-specific allocator 가 메모리를 할당하는데 사용된다.

 

객체에 object-specific allocator 가 없고 512 byte 이상의 메모리가 요청된 경우 Python 메모리 관리자는 Raw memory Allocator 를 직접 호출하여 메모리를 할당한다.

 

요청된 메모리 크기가 512 byte 미만일 때, Object Allocator 를 사용하여 할당한다.

 

 

 

 

 

 

 

 

 

 

 

 

Object Allocator

object allocator 는 pymalloc이라고도 하며, 크기가 512 byte 미만인 작은 개체에 메모리를 할당하는 데 사용된다.

CPython 코드베이스는 object allocator 를 다음과 같이 설명한다.

general-purpose malloc 위에 사용되는 작은 블록을 위한 빠른 특수 목적 메모리 할당자.
object-specific allocator 가 독점 할당 체계를 구현하지 않는 한 모든 객체 할당/할당 해제에 대해 호출된다.
(예: int 는 간단한 free 리스트를 사용함)

이는 순환 가비지 컬렉터가 컨테이너 객체에서 선택적으로 작동하는 곳이기도 하다.

작은 객체가 메모리를 요청하면 해당 객체에 대한 메모리를 할당하는 대신, 객체 할당자가 운영체제에서 큰 메모리 블록을 요청한다. 이 큰 메모리 블록은 나중에 다른 작은 객체에 메모리를 할당하는 데 사용된다.

CPython 코드베이스에서 발췌한 내용에 따르면

이런식으로 객체 할당자는 각각의 작은 객체에 대해 malloc 을 직접 호출하는 것을 방지한다. 

 

객체 할당자가 할당하는 큰 메모리 블록을 Arena 라고 한다. Arena 의 크기는 256KB 이다.

 

Arena 를 효율적으로 사용하기 위해서 CPython 은 Arena 를 Pool(4KB) 로 나눈다. 따라서 Arena 는 64개의 Pool 로 구성될 수 있다.

 

Pool 은 다시 블록으로 나뉜다.

다음 장에서는 이러한 각 구성 요소에 대해 설명한다.

 

 

 

 

 

 

 

 

 

Blocks

블록은 object allocator 가 객체에 할당할 수 있는 가장 작은 메모리 단위이다. 블록은 하나의 객체에만 할당될 수 있고, 객체 또한 하나의 블록에만 할당될 수 있다. 즉, 객체의 일부를 두 개 이상의 개별 블록에 배치하는 것은 불가능하다.

 

 

블록은 다양한 크기로 제공된다. 블록의 최소 크기는 8 byte 이고, 최대 크기는 512 byte 이다. 블록 크기는 8의 배수로 8, 16, 24, 32, ..., 504, 512 바이트가 될 수 있다. 각 블록 크기를 Size Class 라고 한다. Size Class 는 아래와 같이 64 개의 등급으로 나뉜다.

 

이 표에서 볼 수 있듯이 size class 0 블록의 크기는 8 byte 이고, size class 1 블록의 크기는 16 byte 이다.

 

프로그램은 항상 블록 하나 전체에 할당되거나, 아예 할당이 되지 않거나 두 가지 상태만 존재한다. 즉, 프로그램이 14 byte 의 메모리를 요청하면 16 byte 의 블록이 할당된다. 마찬가지로 프로그램이 35 byte 의 메모리를 요청하면 40 byte 의 블록이 할당된다.

 

 

 

 

 

 

 

 

 

Pools

풀은 size class 가 하나인 블록들로 구성된다. 예를 들어 size class 0 의 블록이 있는 풀은 다른 size class 의 블록을 가질 수 없다.

풀의 크기는 가상 메모리 페이지의 크기와 같다.

가상 메모리 페이지:
페이지, 메모리 페이지, 또는 가상 페이지는 가상 메모리의 고정 길이 연속 블록이다.
가상 메모리 운영 체제에서 메모리 관리를 위한 가장 작은 데이터 단위이다.
- Wikipedia

대부분의 경우, 풀 크기는 4KB 이다.

풀은 요청된 size class 의 블록이 있는 사용 가능한 다른 풀이 없는 경우에만 아레나에서 잘라낸다.

풀은 다음 세 가지 상태 중 하나일 수 있다.

  • Used: 풀에 할당에 사용된 블록이 있는 경우 사용됨 상태에 있다고 한다.
  • Full: 풀의 모든 블록이 할당된 경우 풀이 가득찬 상태에 있다고 한다.
  • Empty: 풀의 모든 블록을 할당할 수 있는 경우, 풀이 비어있는 상태라고 한다. 빈 풀에는 size class 개념이 없으며, 모든 size class 등급의 블록을 할당하는 데 사용할 수 있다.

 

풀은 CPython 코드에서 아래와 같이 정의된다:

/* Pool for small blocks. */
struct pool_header {
    union { block *_padding;
            uint count; } ref;          /* number of allocated blocks    */
    block *freeblock;                   /* pool's free list head         */
    struct pool_header *nextpool;       /* next pool of this size class  */
    struct pool_header *prevpool;       /* previous pool       ""        */
    uint arenaindex;                    /* index into arenas of base adr */
    uint szidx;                         /* block size class index        */
    uint nextoffset;                    /* bytes to virgin block         */
    uint maxnextoffset;                 /* largest valid nextoffset      */
};
  • szidx: 풀의 size class를 나타낸다. 풀에 대해 szidx 가 0이면 size class 0 의 블록, 즉 8 byte 블록만 포함된다.
  • arenaindex: 풀이 속한 arena 의 인덱스를 나타낸다.
  • 동일한 size class 의 풀은 이중 연결 리스트를 사용하여 서로 연결된다.
  • nextpool: 같은 size class 의 다음 풀을 가리키는 포인터
  • prevpool: 같은 크기 클래스의 이전 풀을 가리키는 포인터

 

동일한 size class 의 풀이 연결되는 방법은 아래 그림과 같다.

 

  • freeblock: 풀 내에서 사용가능한 블록의 단일 연결 리스트의 시작을 가리키는 포인터로, 할당된 블록이 해제되면 freeblock 포인터 앞에 추가된다.

왼쪽 그림에서 볼 수 있듯이, allocated 블록은 객체에 할당된 상태이고, free 블록은 개체에 할당되었지만 이제 새 개체에 할당 가능한 블록이다.

메모리에 대한 요청이 생기면, 요청된 size class 의 블록을 사용할 수 있는 풀이 없는 경우 CPython 은 Arena 에서 새 풀을 만든다고 했다.

그렇다고 해서 새 풀이 만들어지면 전체 풀이 즉시 블록으로 설정되는 것이 아니다. 블록은 필요에 따라 풀에서 만들어질 수 있다.

위 그림에서 풀의 파란색 영역은 해당 부분이 블록으로 아직 만들어지지 않았음을 나타낸다.

 

 

 

 

 

CPython 코드베이스에서 발췌한 내용에 따르면

블록조각:
풀에서 사용 가능한 블록은 풀이 초기화될 때 (블록끼리)함께 연결되지 않는다.
대신 가장 낮은 주소를 가지는, 처음 두개 블록만 설정되어 첫 번째 블록을 반환하고, pool->freeblock 을 두 번째 블록을 포함하는 단일 블록 리스트로 설정한다. 이는 pymalloc 이 실제로 필요할 때까지 메모리 조각을 건드리지 않으려고 아레나, 풀 및 블록 등 모든 수준에서 노력하는 것이다.

 

우리는 아레나에서 새 풀을 만들 때 풀에서 처음 두 블록만 만들어지는 것을 볼 수 있다.

블록 하나는 메모리를 요청한 객체에 할당되고, 다른 블록은 사용 가능하거나 변경되지 않는다. freeblock 포인터가 이 사용되지 않은 블록을 가리킨다.

 

 

 

 

 

 

CPython 은 모든 size class 의 used 상태에서 풀을 추적하기 위해 usedpools 라는 배열을 유지 및 관리한다. 이때 used 상태는 할당에 사용되었지만, 현재는 할당에 사용 가능한 상태를 일컫는다.

usedpools 배열의 인덱스는 풀의 size class 인덱스와 동일하다. usedpools 배열의 각 인덱스 i 에 대해 usedpools[i] 는 size class i 의 풀 헤더를 가리킨다.

예를 들어, usedpools[0] 은 size class 0 의 풀 헤더를 가리키고, usedpools[1] 은 size class 1 의 풀 헤더를 가리킨다.

 

아래 그림을 보면 더 이해하기 쉬울 것 같다:

동일한 size class 의 풀은 이중 연결 리스트를 사용하여 서로 연결되어 있으므로, 각 size class 의 used 상태에 있는 모든 풀은 usedpools 배열을 사용하여 순회할 수 있다.

usedpools[i] == null 이면 used 상태의 size class i 인 풀이 없음을 의미한다. 객체가 size class i 의 블록을 요청하면 CPython 은 size class i 의 새 풀을 만들고 usedpools[i] 가 이 새 풀을 가리키도록 업데이트 한다.

 

 

 

 

 

 

 

 

 

 

 

 

full 상태의 블록이 free 가 되면, 풀 상태는 full 에서 used 상태로 변경된다. CPython 은 이 풀을 size class 풀의 이중 연결 리스트 앞에 추가한다.

그림과 같이, poolX는 size class 0 이며 full 상태이다. 블록이 poolX에서 해제되면, 상태가 full 에서 used 로 변경되는데, 이때 CPython 은 이 풀을 size class 0 의 풀 이중 연결 리스트의 맨 앞에 추가하고, usedpools[0] 이 poolX 를 가리키도록 한다.

 

 

 

 

 

 

 

 

Arenas

아레나는 작은 개체에 메모리를 할당하는 데 사용되는 큰 메모리 블록이다.

이들은 raw memory allocator 에 의해 256KB 크기로 할당된다. 

 

작은 객체가 메모리를 요청하되, 이 요청을 처리할 기존 영역이 없는 경우 raw memory allocator 는 운영 체제에서 큰 메모리 블록 256KB 를 요청한다. 이러한 큰 메모리 블록을 아레나라고 한다. (그리고 풀은 필요할 때 아레나에서 만들어진다.)

 

CPython 코드베이스에 정의된 arena_object 를 살펴보자:

struct arena_object {
    /* The address of the arena, as returned by malloc */ 
    uintptr_t address;

    /* Pool-aligned pointer to the next pool to be carved off. */
    block* pool_address;

    /* The number of available pools in the arena:  free pools + never-
     * allocated pools.
     */
    uint nfreepools;

    /* The total number of pools in the arena, whether or not available. */
    uint ntotalpools;

    /* Singly-linked list of available pools. */
    struct pool_header* freepools;

    struct arena_object* nextarena;
    struct arena_object* prevarena;
};

static struct arena_object* usable_arenas = NULL;
  • freepools: free 풀 목록을 가리키는 포인터이며, free 풀에는 할당된 블록이 없는 상태이다.
  • nfreepools: 아레나의 free 풀 수를 나타낸다.
  • useable_arenas: CPython 은 사용 가능한(empty 또는 used 상태) 풀이 있는 모든 아레나를 추적하기 위해 useable_arena 라는 이중 연결 리스트를 유지 및 관리한다. useable_arenas 리스트는 nfreepools 값의 오름차순으로 정렬된다.
  • nextarena: 다음 사용 가능한 아레나를 가리키는 포인터
  • prevarena: useable_arenas 이중 연결 리스트에서 이전 사용 가능한 아레나를 가리키는 포인터

위 그림에서 볼 수 있듯이, useable_arenas 는 nfreepools 를 기준으로 정렬된다.

0개의 free 풀이 있는 아레나가 첫 번째 항목이고, 그 다음에는 1개의 free 풀이 있는 아레나가 위치하는 방식이다.

이는 리스트가 가장 많이 할당된 아레나부터 먼저 위치함을 의미한다. 그 이유는 다음 섹션에서 설명할 것이다.

 

사용 가능한 아레나 리스트는 어떤 아레나가 가장 많이 할당 되었는지를 기준으로 정렬되므로, 메모리 할당 요청 시 할당이 가장 많은(nfreepools가 가장 적은) 아레나에서 서비스된다.

 

Does a Python Process Release Memory?

풀에 할당된 블록이 해제되면 CPython은 메모리를 운영체제로 다시 반환하지 않는다. 이 메모리는 계속 Python 프로세스에 속하며, CPython 은 이 블록을 사용하여 새 개체에 메모리를 할당한다.

 

풀의 모든 블록이 해제되더라도 CPython 은 풀에서 운영체제로 메모리를 반환하지 않는다. CPython 은 자체 사용을 위해 할당되었던 전체 풀의 메모리를 유지한다.

 

그리고 CPython 은 블록이나 풀 수준이 아닌, 아레나 수준에서 운영체제로 메모리를 해제한다. 이때, 전체 아레나의 메모리를 한 번에 해제함에 유의하자.

 

메모리는 아레나 수준에서만 해제될 수 있으므로, CPython 은 절대적으로 필요한 경우에만 새로운 메모리 조각에서 아레나를 생성한다. 즉, 항상 이전에 만들었던 블록과 풀에서 메모리를 할당하려고 시도한다.

 

이것이 useable_arenas 가 nfreepools 의 내림차순으로 정렬된 이유이다. 

메모리에 대한 다음 요청은 할당량이 가장 많은(=남은 free 풀 수가 가장 적은) 아레나에 할당됨으로써, 최소한의 데이터가 포함된 아레나의 객체가 삭제되면 빈 공간이 될 수 있으며, 이러한 아레나는 할당 해제되어 운영체제로 메모리를 반환할 수 있다.

 

 

 

다음 내용은 Python 가비지 컬렉터에 관련된 내용이다.

+ Recent posts