본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 모든 객체의 뿌리, PyObject 헤더 구조의 2가지 핵심 요소와 메모리 관리 방법

by Papa Martino V 2026. 3. 15.
728x90

PyObject 헤더
PyObject 헤더

 

파이썬은 "모든 것이 객체(Everything is an Object)"인 언어입니다. 정수 하나, 함수 하나, 심지어 클래스 정의 자체도 메모리상에서는 하나의 객체로 존재합니다. 그렇다면 파이썬 인터프리터(CPython)는 이 수많은 객체들을 어떻게 일관되게 인식하고 관리할까요? 그 비밀은 모든 파이썬 객체의 최상단에 위치하는 PyObject 헤더에 숨겨져 있습니다. 본 포스팅에서는 파이썬의 심장부라고 할 수 있는 PyObject 구조체의 내부를 해부하고, 메모리 참조 횟수 관리와 타입 시스템이 어떻게 맞물려 돌아가는지 그 해결 원리를 심도 있게 다룹니다.


1. PyObject: 파이썬 객체의 공통 설계도

C언어로 작성된 파이썬의 표준 구현체인 CPython에서 모든 객체는 PyObject라는 구조체를 기본 바탕으로 합니다. 객체가 정수(int)이든 리스트(list)이든 관계없이, 메모리 상의 가장 앞부분은 항상 동일한 레이아웃을 가집니다. 이를 통해 파이썬은 다형성(Polymorphism)을 구현하며, 어떤 객체든 동일한 방식으로 메모리 관리 시스템에 접근할 수 있게 됩니다.


2. PyObject 헤더를 구성하는 2가지 필드

PyObject는 크게 두 가지 핵심 필드로 구성됩니다. 이 정보가 없으면 파이썬은 해당 데이터를 어떻게 처리해야 할지 알 수 없게 됩니다.

(1) ob_refcnt (Reference Count)

해당 객체를 참조하고 있는 변수나 객체의 개수를 나타내는 정수 값입니다. 파이썬 가비지 컬렉션(GC)의 가장 기본이 되는 데이터로, 이 값이 0이 되는 순간 객체는 메모리에서 해제됩니다.

(2) ob_type (Type Object Pointer)

객체의 타입을 정의하는 다른 객체(PyTypeObject)를 가리키는 포인터입니다. 예를 들어 특정 객체가 str 타입이라면, ob_typestr 타입의 정의가 담긴 메모리 주소를 가리킵니다. 이를 통해 파이썬은 런타임에 객체의 메서드나 속성을 찾을 수 있습니다.


3. 일반 객체와 가변 크기 객체의 구조 차이 비교

파이썬 객체는 크기가 고정된 객체와 데이터 양에 따라 크기가 변하는 객체로 나뉩니다. 가변 크기 객체는 PyVarObject를 사용하며, 헤더에 한 가지 정보가 더 추가됩니다.

필드명 구조체 타입 역할 및 기능 포함 객체 예시
ob_refcnt Py_ssize_t 참조 횟수 기록 (메모리 해제 결정) 모든 객체 공통
ob_type struct _typeobject* 객체의 데이터 타입 정보 링크 모든 객체 공통
ob_size Py_ssize_t 가변 객체의 요소 개수(Length) 저장 list, dict, str, tuple

4. Sample Example: C 레벨의 PyObject 정의 들여다보기

실제 CPython 소스 코드(Include/object.h)에 정의된 PyObject의 개념적 구조를 코드로 표현하면 다음과 같습니다. 개발자가 직접 C로 파이썬 확장 모듈을 만들 때 반드시 마주하게 되는 구조입니다.

/* CPython 내부 구조체 개념 예시 */

typedef struct _object {
    _PyObject_HEAD_EXTRA        /* 디버그 모드 시 연결 리스트 포인터 */
    Py_ssize_t ob_refcnt;       /* 참조 횟수: 정수형 */
    struct _typeobject *ob_type; /* 타입 정보 포인터 */
} PyObject;

typedef struct {
    PyObject ob_base;           /* 기본 헤더 포함 */
    Py_ssize_t ob_size;         /* 가변 객체의 경우 크기 정보 추가 */
} PyVarObject;

/* 파이썬에서 'a = 10'을 수행하면 
   메모리에는 PyObject 헤더 + 정수 값(10)을 담은 공간이 할당됩니다. 
*/
    

5. 객체 헤더를 활용한 효율적인 메모리 관리 방법

파이썬 엔진이 헤더를 통해 성능을 최적화하고 문제를 해결하는 방식은 다음과 같습니다.

  1. 빠른 타입 체크: 파이썬의 type() 함수나 isinstance()는 단순히 헤더의 ob_type 포인터를 확인하는 방식으로 매우 빠르게 동작합니다.
  2. 즉각적인 자원 회수: ob_refcnt가 0이 되자마자 할당된 메모리를 즉시 해제하거나(Free), 작은 객체 할당기(pymalloc)의 풀로 반환하여 메모리 파편화를 방지합니다.
  3. 공통 인터페이스: 모든 객체가 같은 헤더를 가지므로, 리스트 내부에 서로 다른 타입의 객체를 섞어서 저장해도(Heterogeneous list) 파이썬은 헤더를 통해 각 요소를 일관되게 처리할 수 있습니다.

6. 결론 및 출처

PyObject 헤더는 파이썬의 동적 유연성과 엄격한 메모리 관리 사이를 잇는 교량 역할을 합니다. 우리가 작성하는 단 한 줄의 코드 뒤에는 수많은 객체 헤더들이 참조 횟수를 계산하고 타입을 대조하며 시스템의 안정성을 유지하고 있습니다. 이러한 내부 구조를 이해하는 것은 파이썬의 성능 병목 현상을 진단하고 더 효율적인 코드를 설계하는 데 있어 강력한 무기가 될 것입니다.

 

참고 문헌 및 출처:

  • CPython Source Code - Include/object.h
  • Python Internals: Objective-C style polymorphism in C (Real Python)
  • Inside the Python Virtual Machine (Obi Ike-Nwosu)
728x90