언어/Python

[Python] Garbage Collection

JM Lee 2024. 3. 5. 02:13
728x90

Python의 언어가 가지는 의미를 이때까지 얕게 이해했음을 이번 Garbage Collection(이하 GC) 공부를 통해 확실하게 느꼈다.

프레임워크를 잘 사용하고, 파이썬 함수/자료구조/알고리즘을 적절히 잘 사용하여 효율적으로 코드를 짜면 1차적으로 좋은 신입이 되지 않을까 생각했는데..

새로운 분야에 눈을 뜬 것 같아서 오히려 좋다.

 

GC는 현대적인 언어에는 거의 필수로 존재하며 개발자의 생산성을 향상해준다. C#, JS, Python 등의 언어는 GC를 기본적으로 제공하며, C, C++과 같은 언어에서는 malloc(), free()와 같은 저수준의 메모리 관리 함수를 제공한다. 실제로 현대적인 언어로 오면서 개발자는 메모리를 직접 관리하는 코드를 거의 사용하지 않게 되었다.(어쩐지 아직 경험해본 적이 없었다..) 경험해본 적이 없기에 이 과정을 면밀히 이해해야, 적절하게 잘 사용할 수 있다.


 

GC가 만들어진 이유

 이전에는 프로그래머가 메모리를 할당하고 해제하는 것에 대한 책임이 있었는데, 이는 실수로 메모리를 해제하지 않거나 이미 해제된 메모리를 다시 사용하는 등의 문제를 일으킬 수 있었다. 이는 프로그램의 안정성과 신뢰성을 저해할 수 있다.

가비지 컬렉션은 이러한 문제를 해결하기 위해 도입되었다. 가비지 컬렉션은 프로그램이 실행되는 동안 더 이상 사용되지 않는 객체를 자동으로 감지하고 해제하여 메모리를 관리한다. 이를 통해 개발자는 메모리 관리에 대한 부담을 줄일 수 있으며, 메모리 누수와 같은 일반적인 문제를 방지할 수 있다. 

 

GC 기능

  1. 자동 메모리 관리: 가장 중요한 기능은 더 이상 필요하지 않은 객체들의 메모리를 자동으로 해제하여 메모리 누수를 방지한다.
  2. 가용한 메모리 최적화: 런타임 중에 메모리 사용량을 모니터링하고 필요할 때마다 사용하지 않는 메모리를 해제하여 가용한 메모리를 최적화한다.
  3. 프로그래머의 부담 감소: 가비지 컬렉션을 사용하면 프로그래머가 메모리 할당과 해제를 명시적으로 관리할 필요가 없으므로 코드 작성이 간단해진다.
  4. 메모리 누수 방지: 가비지 컬렉션은 더 이상 참조되지 않는 객체들을 감지하여 자동으로 해제하므로 메모리 누수를 방지한다.
  5. 효율적인 자원 사용: 가비지 컬렉션은 런타임에 메모리 관리를 수행하므로 프로그램이 더 효율적으로 메모리를 사용할 수 있다.

간단하게 아래에 GC의 예시를 주석과 함께 첨부했다.

import gc

# 순환 참조가 없는 경우
a = [1, 2, 3]  
b = a          
del a          # a의 참조 제거

# 여기서는 a가 제거되었으므로 a의 메모리가 해제된다.
# 하지만 b가 여전히 리스트를 참조하고 있으므로 리스트 객체는 메모리에서 제거되지 않는다.
# 파이썬의 가비지 컬렉터는 이러한 참조를 계속 추적하여 필요 없는 객체를 제거한다.

파이썬에서의 GC 방식

1 . 레퍼런스 카운팅(Reference Counting)

  • 각 객체가 참조되는 횟수를 추적하여 해당 객체가 더 이상 필요하지 않을 때 메모리에서 해제하는 방법
  • 객체가 생성될 때 해당 객체의 참조 카운트가 1로 시작
  • 이 객체를 다른 변수, 객체에 할당하거나 컨테이너(예: 리스트, 딕셔너리 등)에 추가하면 해당 객체의 참조 카운트가 증가
  • 반대의 경우는 감소
  • 참조 카운트가 0이되면 해당 객체는 더 이상 사용되지 않으므로 파이썬 인터프리터는 해당 객체의 메모리를 해제
  • 순환 참조의 문제는 해결할 수 없음
    • 숫자가 0이 될 수 없기 때문에 제거되지 않는다.

2. 세대별 가비지 컬렉션(Generational Garbage Collection)

  • 메모리 관리를 위해 객체를 세대로 분류, 일반적으로 아래 3개 세대로 분류 
  • 새로 생성된 객체 (Young Generation): 대부분의 객체는 이 세대에 속한다. 이 세대의 객체들은 짧은 수명을 가지고 있다.
  • 중간 수명의 객체 (Intermediate Generation): Young Generation에서 일정 횟수의 가비지 컬렉션을 거치고 살아남은 객체들이 이 세대로 이동하게 된다.
  • 오래된 객체 (Old Generation 또는 Tenured Generation): 오랫동안 살아남은 객체들이 이 세대에 속한다. 이 세대의 객체들은 가비지 컬렉션의 대상이 되는 경우가 적다.
  • 오래된 세대일수록 검증된 객체라고 판단했기 때문에 위 매커니즘이 발동 가능

 

Manual Garbage Collection

Manual Garbage Collection(수동 가비지 컬렉션)을 수행하는 방법은 두 가지가 있다.

  1. Time-based(시간 기반): 가비지 컬렉터를 고정된 시간 간격마다 호출하는 것이다.
  2. Event-based(이벤트 기반): 이벤트 발생 시 가비지 컬렉터를 호출한다.
    예를 들어, 사용자가 응용 프로그램을 종료하거나 응용 프로그램이 중단 상태일 때 호출하는 것이다.

 

결론

웬만하면 자동 GC를 그대로 냅두는 것이 좋다고 한다. 위의 예시처럼 import gc를 통해 수동으로 트리거할 수 있지만, 시간복잡도가 훨씬 오른다. 자동 GC가 컴퓨팅 자원(공간)을 더 많이 차지하지만, 아무래도 중요도는 시간>공간이기 때문에 웬만하면 자동으로 두는 것이 더 좋다.

유의할 점은 GC 매커니즘으로 순환참조 오류를 완전히 해결하지 못한다. 그렇기 때문에 절대적으로 필요하지 않다면 순환참조로 인한 메모리 누수가 최소화될 수 있도록 주의하자.