본문 바로가기

공부/Python

Python - 메모리 관리 gc.collect() vs malloc_trim()

메모리가 부족해 곤란한 모습..

파이썬은 개발자가 직접 메모리를 할당하거나 해제할 필요가 없는 고수준의 프로그래밍 언어로 파이썬에서는 내부적으로 메모리 관리를 자동으로 처리한다고 생각해와서 메모리 관리를 신경쓰지 않고 개발을 해왔었다... 

 

최근 파이썬 코드를 실행하는 도커 컨테이너에서 메모리가 증가하고 반환되지 않는 문제를 발견하여 파이썬이 메모리를 관리하는 내부 메커니즘에 대해 자세하게 공부해보았다. 그리고 추가로 ... 해당 문제가 메모리 누수가 아닌 Linux의 특징이라는 새로운 사실도 알게되었다.

 

 

메모리의 구조

 

 

코드 영역 - 스크립드의 바이트코드와 실행될 프로그램의 기계어 코드가 저장됨.

데이터 영역 - 정적 변수와 전역 변수가 저장됨.

힙 영역 - 동적 메모리 할당이 이루어 지는 곳

스택 영역 - 로컬변수, 매개변수, 반환 주소 및 기타 함수의 실행 정보가 저장됨.

 

Python에서 모든 것은 객체(object)이다.

 

파이썬의 핵심적인 특성 중 하나로 파이썬에서는 사용되는 데이터 유형이나 구조가 모두 객체로 표현된다.

 

이때 파이썬의 모든 객체는 heap 영역에 저장되게 되고, 선언된 변수들은 heap에 있는 객체를 참조하게 된다.

이게 핵심인데, 예를 들어서 설명해보면

a = '깨비'
print(id(a))
# 2278330450768
b = '깨비'
print(id(b))
# 2278330450768

변수 a, b는 '깨비' 라는 동일한 객체를 참조하고 있는 것이다.

import sys
a = '깨비'
print(sys.getrefcount(a))
# 4
b = '깨비'
print(sys.getrefcount(a))
# 5

'깨비' 라는 객체의 참조 카운트를 확인해보면 b 변수가 선언되고 +1 된 것을 확인할 수 있음.

 

이때 참조 카운트가 4로 시작하는 것은 파이썬 메모리 할당시 레퍼런스카운트는 윈도우= 2, 리눅스= 3으로 시작하게되고, 여기에서 a로 다시 참조해서 4가 된다고 한다. 추가로 문자/숫자의 경우 일반 .py를 실행 했을 경우에 인터프린터에서 미리 참조 (인터닝)을 위해 한번 더 읽는 과정(이때 한번 더 참조)을 거쳐서 숫자가 하나 더 높게 나온다고 한다.

Python의 메모리 관리 방식

파이썬은 가비지 컬렉션과 자동 참조 카운팅 메커니즘으로 내부적으로 메모리 관리를 자동으로 처리한다.

 

자동 참조 카운팅 - 각 객체의 참조 카운팅이 0이 되면, 해당 객체를 자동으로 메모리에서 해제한다.

 

가비지 컬렉션 - 참조 카운팅만으로는 해결할 수 없는 순환 참조 문제를 해결하기 위해 주기적으로 메모리를 스캔해 객체들을 찾아내고 해제한다. gc 모듈을 통해 수동으로 실행할 수 있다.

 

+ Linux 환경에서 Python 메모리 관리 

Linux 기반의 도커 컨테이너에서 파이썬 코드를 실행하면 메모리가 반환되지 않고 증가하는 것을 확인했다. 명시적으로 gc.collect()를 통해 가비지 컬렉션을 호출하고 메모리 누수가 어디서 발생하는지 코드를 하나하나씩 모두 뜯어보았는데 답을 찾을 수 없었다... (해당 과정에서 위의 내용도 같이 학습하게됨...)

 

그런데 희한하게 로컬 환경에서는 해당 코드를 실행하면 메모리 반환이 정상적으로 잘되는 것을 확인하고, 파이썬 프로세스가 실행되는 OS의 차이를 의심했다. (처음엔 내 코드 아니면 라이브러리가 문제라고 생각하고 2주 정도 삽질함.. ㅜㅜ)

 

문제의 원인을 환경의 차이에 초점을 맞추고 찾아보니 아래의 글을 찾을 수 있었다.

 

"Python returns memory to the OS on the heap (that allocates other objects than small objects) only on Windows, if you run on Linux, you can only see the total memory used by your program increase."

 

Large Dictionaries Not Released From Memory - Python + Ubuntu

I have a flask application running on Ubuntu which reads from a file and creates several large dictionaries. After the dictionaries are created and flask returns the request, the memory used for the dictionaries is not released back to the OS. This example

www.paulsprogrammingnotes.com

실제로 메모리가 증가한 상태에서 메모리 dump를 떠 확인해보면 딕셔너리가 증가해있었음.

요지는 리눅스에서는 해제된 객체가 OS로 반환되지 않고 프로세스가 재사용을 위해 보관한다는 것 이다.

 

gc.collect() vs malloc_trim()

리눅스 환경에서 gc.collect()를 실행하면 힙에서 사용하지 않는 객체가 해제되지만 OS로 반환되지 않고 프로세스가 재사용을 위해 보관하게 된다. 즉 메모리 누수가 발생하는게 아님. 해제된 메모리를 운영체제로 다시 돌려보내 프로세스의 메모리 사용량을 줄이고자 한다면 malloc_trim() 함수를 통해 OS로 메모리를 반환할 수 있다.

 

참고자료

https://woochan-autobiography.tistory.com/867

 

Python - 메모리 구조 및 메모리 할당 과정

목차 Everything is object in Python 파이썬의 메모리 구조 파이썬에서의 Heap 사용 파이썬에서의 메모리 할당 과정 Everything is object in Python x = 10 print(type(x)) >> C에서 x = 10 이렇게 변수를 할당하면, 메모리

woochan-autobiography.tistory.com

https://www.paulsprogrammingnotes.com/2014/10/large-dictionaries-not-released-from.html

 

Large Dictionaries Not Released From Memory - Python + Ubuntu

I have a flask application running on Ubuntu which reads from a file and creates several large dictionaries. After the dictionaries are created and flask returns the request, the memory used for the dictionaries is not released back to the OS. This example

www.paulsprogrammingnotes.com

https://stackoverflow.com/questions/51938963/python-memory-not-being-released-on-linux

 

Python memory not being released on linux?

I am trying to load a large json object into memory and then perform some operations with the data. However, I am noticing a large increase in RAM after the json file is read -EVEN AFTER the object...

stackoverflow.com

https://jay-ji.tistory.com/105

 

어느날 신입 개발자가 나에게 물었다..."python에서 staticmethod를 사용하는 것에 있어서 메모리 이슈

제곧내(제목이 곧 내용)입니다! ㅋㅋㅋ 최근 저희팀에 합류하신 ㅅㄱ님께서 개발 중 이런 질문을 하셨습니다. 사실 이질문을 듣고 라는 생각이 먼저 떠올랐습니다ㅠㅠ 그리고 python을 메인으로

jay-ji.tistory.com

https://pearlluck.tistory.com/758

 

🧐 파이썬 코드를 잘 짜는 법 : 메모리 구조와 메모리 할당방식 이해

사실 지금 코틀린을 공부해야만 하는 상황인데, 갑자기 파이썬 글 쓰기..ㅎㅎ 역시 사람은 모순적인 동물이야..나는 청개구리 그전에 오래전에 내가 썼던 자바와 파이썬의 차이점을 먼저 되짚어

pearlluck.tistory.com