GIL
- GIL은 python의 메모리 관리와 깊은 연관이 있다. 따라서 python의 메모리 관리에 대해서 언급하고 넘어가고자 한다.
python의 메모리 관리
파이썬은 C, C++가 프로그래머가 직접 메모리를 관리하는 것과는 달리, 레퍼런스 카운트(Reference Counts)와 가비지 콜렉션(Automatic Garbage Collection)에 의해 관리된다.
레퍼런스 카운트(Reference Counts)
파이썬은 내부적으로 malloc()과 free()를 많이 사용하기 때문에 memory leak의 위험이 있다. 이러한 이유로 파이썬은 메모리 관리를 위해 레퍼런스 카운트를 사용한다.
레퍼런스 카운트란
- 객체에 카운트를 포함
- 카운트는 객체가 참조될 때 증가
- 참조가 삭제될 때 감소
- 카운터가 0이 되면 메모리 할당 삭제
하지만 레퍼런스 카운트만으로 메모리를 관리했을 때에 약점이 있는데, 그것은 순환 참조이다.
순환 참조란 컨테이너 객체가 자기 자신을 참조하는 것이다. 자기 자신을 참조할 때 레퍼런스 카운트가 0에 도달할 수 없고 할당된 메모리를 삭제할 수 없어 memory leak이 발생한다.
파이썬은 이러한 문제를 가비지 콜렉션을 이용하여 해결한다.
가비지 콜렉션(Automatic Garbage Collection)
파이썬의 gc
모듈을 통해 가비지 콜렉터를 직접 제어할 수 있다. 파이썬에서는 Cyclic Garbage Collection을 지원하고, 이것은 참조 주기를 감지하여 메모리 누수를 예방한다.
- 가비지 컬렉션의 기준
가비지 컬렉터는 generation
과 threshold
로 가비지 컬렉션 주기와 객체를 관리한다.
0세대, 1세대, 2세대로 총 3개의 영역으로 구분되며, 0세대에 가까울 수록 최근 생성된 객체, 2세대에 가까울수록 오래된 객체이다. generational hypothesis
에 따라 가비지 컬렉터는 0세대일수록 자주 가비지 컬렉션을 하도록 설계되어 있다.
- 순환 참조 감지 방법
순환참조는 컨테이너 객체(tuple
, list
, set
, dict
, class
) 에서만 발생할 수 있다. 순환참조 해결을 위해 모든 컨테이너 객체를 추적한다. 추가적인 메모리 할당을 하지 않기 위해 객체 내부 link field에 double linked list를 사용될 것이다. 컨테이너 객체가 생성될 때 컨테이너 객체 집합에 추가되고, 제거될 때 집합에서 삭제된다.
1. 객체 gc_refs 필드를 레퍼런스 카운트와 같게 설정
2. 각 객체에서 참조하고 있는 다른 컨테이너 객체를 찾고, 참조되는 컨테이너의 gc_refs를 감소시킨다.
3. gc_refs 가 0이면 컨테이너 집합 내부에서 자기들끼리 참조하고 있다는 것이므로 메모리에서 해제한다.
다시, GIL
-
GIL은 한번에 하나의 스레드만 수행할 수 있도록 인터프리터에 lock을 거는 기능이다.
-
파이썬 객체는 가비지 컬렉션 기능을 위해, 레퍼런스 카운트를 가지고 있는데, 위에서 설명했듯이 해당 객체를 참조할 때마다 해당 값을 증가하고, 참조가 삭제될 때 감소해야 한다.
-
멀티 스레드를 실행하게 되면, 각 thread가 공유하는 객체들에 대해 lock을 거는 경우(mutex), reference count를 변경하는 것에 있어 deadlock의 위험이 존재하기 때문에, 인터프리터 레벨에서 한 시점에 실행하는 스레드는 1개로 제한한 것이다.
-
따라서 멀티 CPU 환경일지라도, 파이썬 thread는 1개만 실행된다.
-
이를 해결하기 위해서 multiprocessing 라이브러리를 사용하면 개별 프로세스가 생성되고, 프로세스 별로 인터프리터 lock이 걸리기 때문에 동시에 실행이 가능하다.
-
병렬처리에 관해서 굳이 thread가 아니더라도 multiprocessing이나 asyncio 등 많은 선택지가 있으며, 아직까지 단일 thread에서 성능을 저하시키지 않고 GIL을 개선할 수 있는 방안이 없기 때문에 GIL은 계속 유지되어 오고 있다.
Reference
'Language > Python' 카테고리의 다른 글
pip install - killed (0) | 2020.11.14 |
---|---|
requests.exceptions.ConnectionError: ('Connection aborted.', BadStatusLine('ICY 401 Service Unavailable\r\n')) (1) | 2020.03.09 |