파이썬에서 메모리 해제


128

다음 예제에서 메모리 사용에 관한 몇 가지 관련 질문이 있습니다.

  1. 통역사를 뛰면

    foo = ['bar' for _ in xrange(10000000)]

    내 컴퓨터에서 사용 된 실제 메모리는로 올라갑니다 80.9mb. 그런 다음

    del foo

    실제 메모리는 다운되지만로만 내려갑니다 30.4mb. 인터프리터는 4.4mb기준을 사용 하므로 26mb메모리를 OS에 공개하지 않는 이점은 무엇 입니까? 파이썬이 "미리 계획 중"이기 때문에 많은 메모리를 다시 사용할 수 있다고 생각합니까?

  2. 50.5mb특히 출시됩니까? 출시되는 금액은 얼마입니까?

  3. 파이썬이 사용 된 모든 메모리를 강제로 해제 할 수있는 방법이 있습니까? (많은 메모리를 다시 사용하지 않을 경우)?

참고이 질문은 파이썬에서 명시 적으로 메모리를 비울 수있는 방법 과 다릅니다 . 이 질문은 인터프리터가 가비지 수집을 통해 객체를 해제 한 후에도 (기준 사용 여부에 관계없이) 기준선에서의 메모리 사용량 증가를 주로 다루기 때문 gc.collect입니다.


4
이 동작은 Python에만 국한되지 않습니다. 프로세스가 일부 힙 할당 메모리를 해제 할 때 프로세스가 종료 될 때까지 메모리가 OS로 다시 해제되지 않는 경우가 일반적입니다.
NPE

귀하의 질문은 여러 가지를 요구합니다. 그 중 일부는 멍청하고 일부는 SO에 부적합하며 일부는 좋은 질문 일 수 있습니다. 파이썬이 메모리를 해제하지 않는지, 정확히 어떤 상황에서, 불가능한지, 기본 메커니즘이 무엇인지, 왜 그런 식으로 설계되었는지, 해결 방법이 있는지 또는 완전히 다른 것이 있는지 묻고 있습니까?
abarnert

2
@ abarnert 나는 비슷한 하위 질문을 결합했습니다. 귀하의 질문에 대답하기 위해 : 파이썬이 OS에 약간의 메모리를 릴리스하지만 모든 것이 아닌 이유와 그 정도를 알고 있습니다. 그것이 불가능한 상황이 있다면 왜? 어떤 해결 방법도 있습니다.
Jared


@jww 나는 그렇게 생각하지 않습니다. 이 질문은 실제로 호출로 가비지를 완전히 수집 한 후에도 인터프리터 프로세스가 메모리를 해제하지 않은 이유와 관련이 gc.collect있습니다.
Jared

답변:


86

힙에 할당 된 메모리는 최고 사용 표시 점이 될 수 있습니다. 이는 PyObject_Malloc4 개의 KiB 풀에 작은 객체 ( ) 를 할당하기위한 Python의 내부 최적화에 의해 복잡해지며 8 바이트의 배수에서 최대 256 바이트 (3.3의 경우 512 바이트)의 할당 크기로 분류됩니다. 풀 자체는 256 KiB 경기장에 있으므로 한 풀에 하나의 블록 만 사용하면 전체 256 KiB 경기장이 해제되지 않습니다. Python 3.3에서는 작은 객체 할당자가 힙 대신 익명 메모리 맵을 사용하도록 전환되었으므로 메모리를 해제 할 때 더 잘 수행되어야합니다.

또한 내장 유형은 작은 객체 할당자를 사용하거나 사용하지 않을 수있는 이전에 할당 된 객체의 프리리스트를 유지합니다. 이 int유형은 자체 할당 된 메모리로 사용 가능 목록을 유지 보수하고이를 지우려면을 호출해야합니다 PyInt_ClearFreeList(). 전체를 수행하여 간접 적으로 호출 할 수 있습니다 gc.collect.

이렇게 해보고 당신이 얻는 것을 말해주십시오. psutil.Process.memory_info에 대한 링크는 다음과 같습니다 .

import os
import gc
import psutil

proc = psutil.Process(os.getpid())
gc.collect()
mem0 = proc.get_memory_info().rss

# create approx. 10**7 int objects and pointers
foo = ['abc' for x in range(10**7)]
mem1 = proc.get_memory_info().rss

# unreference, including x == 9999999
del foo, x
mem2 = proc.get_memory_info().rss

# collect() calls PyInt_ClearFreeList()
# or use ctypes: pythonapi.PyInt_ClearFreeList()
gc.collect()
mem3 = proc.get_memory_info().rss

pd = lambda x2, x1: 100.0 * (x2 - x1) / mem0
print "Allocation: %0.2f%%" % pd(mem1, mem0)
print "Unreference: %0.2f%%" % pd(mem2, mem1)
print "Collect: %0.2f%%" % pd(mem3, mem2)
print "Overall: %0.2f%%" % pd(mem3, mem0)

산출:

Allocation: 3034.36%
Unreference: -752.39%
Collect: -2279.74%
Overall: 2.23%

편집하다:

시스템에서 다른 프로세스의 영향을 제거하기 위해 프로세스 VM 크기를 기준으로 측정으로 전환했습니다.

C 런타임 (예 : glibc, msvcrt)은 상단의 연속 여유 공간이 일정하고 동적이거나 구성 가능한 임계 값에 도달하면 힙을 줄입니다. glibc를 사용하면 mallopt(M_TRIM_THRESHOLD)로 조정할 수 있습니다 . 이를 감안할 때 힙이 블록보다 더 많이 줄어든 것은 놀라운 일이 아닙니다 free.

3.x range에서는 목록을 만들지 않으므로 위의 테스트에서는 천만 개의 int개체를 만들지 않습니다 . 그렇게하더라도 int3.x 의 유형은 기본적으로 2.x long이며 프리리스트를 구현하지 않습니다.


사용 memory_info()대신 get_memory_info()하고는 x정의
아지즈 알토

int파이썬 3에서도 10 ^ 7을 얻지 만 각각 루프 변수의 마지막 변수를 대체하므로 한 번에
Davis Herring

메모리 누수 문제를 만났으며 여기에서 귀하가 응답 한 이유를 추측합니다. 하지만 내 추측을 어떻게 증명할 수 있습니까? 많은 수영장이 malloced되었음을 표시 할 수있는 도구가 있습니까?하지만 작은 블록 만 사용됩니까?
ruiruige1991

130

나는 당신이 정말로 관심있는 질문은 다음과 같습니다.

파이썬이 사용 된 모든 메모리를 강제로 해제 할 수있는 방법이 있습니까? (많은 메모리를 다시 사용하지 않을 경우)?

아니 없어. 그러나 자식 프로세스라는 쉬운 해결 방법이 있습니다.

5 분 동안 500MB의 임시 저장 공간이 필요하지만 그 후 2 시간 더 실행해야하는데 다시 많은 메모리를 건드리지 않으면 하위 프로세스를 생성하여 메모리 집약적 인 작업을 수행하십시오. 자식 프로세스가 사라지면 메모리가 해제됩니다.

이것은 사소하고 자유롭지는 않지만 꽤 쉽고 저렴하며 일반적으로 거래 가치가 충분합니다.

먼저 자식 프로세스를 만드는 가장 쉬운 방법은 다음을 사용하는 것입니다 concurrent.futures(또는 3.1 이전의 경우 futuresPyPI 의 백 포트).

with concurrent.futures.ProcessPoolExecutor(max_workers=1) as executor:
    result = executor.submit(func, *args, **kwargs).result()

좀 더 제어가 필요한 경우 multiprocessing모듈을 사용하십시오 .

비용은 다음과 같습니다.

  • 일부 플랫폼, 특히 Windows에서는 프로세스 시작이 느립니다. 우리는 몇 분이 아닌 밀리 초를 말하고 있으며, 한 아이를 300 초 분량의 일로 돌린다면 눈치 채지 못할 것입니다. 그러나 무료는 아닙니다.
  • 실제로 사용하는 많은 양의 임시 메모리가 경우이 작업을 수행하면 기본 프로그램이 교체 될 수 있습니다. 물론 장기적으로 시간을 절약 할 수 있습니다. 메모리가 영원히 멈 추면 어느 시점에서 스왑이 발생하기 때문입니다. 그러나 이로 인해 일부 사용 사례에서 점진적인 속도 저하가 눈에 띄게 한 번에 (그리고 일찍) 지연 될 수 있습니다.
  • 프로세스간에 많은 양의 데이터를 전송하면 느려질 수 있습니다. 다시 말하지만, 2K 이상의 인수를 전송하고 64K의 결과를 되 찾는 것에 대해서는 이야기조차하지 않지만 대량의 데이터를 보내고받는 경우 다른 메커니즘을 사용하고 싶을 것입니다 ( mmapped 파일 또는 기타 파일 ;의 공유 메모리 API multiprocessing등).
  • 프로세스간에 많은 양의 데이터를 전송하면 (파일이나 공유 메모리, 그들을 스틱 경우, 또는 데이터가 pickleable해야 의미 struct-able 또는 이상적으로 ctypes-able).

문제를 해결하지는 못했지만 정말 좋은 트릭입니다. (그러나 나는 정말로 그것을 좋아합니다
ddofborg

32

eryksun 님은 1 번 질문에 대한 답변을 보았으며, 3 번 질문 (원본 # 4)에 답변했지만 이제 2 번 질문에 답변하겠습니다 :

특히 50.5mb를 릴리스하는 이유는 무엇을 기준으로 릴리스됩니까?

그것이 기반으로하는 것은 궁극적으로 파이썬 내부의 일련의 우연의 일치이며 malloc예측하기가 매우 어렵습니다.

첫째, 메모리를 측정하는 방법에 따라 실제로 메모리에 매핑 된 페이지 만 측정 할 수 있습니다. 이 경우, 호출기가 페이지를 교체 할 때마다 메모리가 해제되지 않았더라도 메모리가 "사용 가능"으로 표시됩니다.

또는 사용중인 페이지를 측정하고있을 수 있지만 할당되지는 않았지만 터치하지 않은 페이지 (리눅스와 같이 낙관적으로 과도하게 할당 된 시스템), 할당되었지만 태그가 지정된 페이지 MADV_FREE등을 계산하거나 계산하지 않을 수 있습니다 .

실제로 할당 된 페이지 (실제로는 매우 유용한 일이 아니지만 요청하는 것 같습니다)를 측정하고 있고 페이지가 실제로 할당 해제 된 경우 두 가지 상황이 발생할 수 있습니다. brk현재는 데이터 세그먼트를 축소하는 데 사용 되거나 이와 동등하거나 (매우 드물게) munmap매핑 된 세그먼트를 해제하는 데 사용 하거나 유사합니다. (매핑 된 세그먼트의 일부를 해제하는 방법이 있다는 점에서 이론적으로 후자에 대한 작은 변형이 있습니다. 예를 들어, 즉시 매핑을 해제 MAP_FIXED하는 MADV_FREE세그먼트에 대해 도용하십시오 .)

그러나 대부분의 프로그램은 메모리 페이지에서 항목을 직접 할당하지 않습니다. 그들은 malloc스타일 할당자를 사용합니다 . 을 호출 free하면 할당 자 free(또는 데이터 세그먼트의 마지막 N 페이지)에서 마지막 라이브 객체를 사용하는 경우 할당자가 OS로 페이지를 릴리스 만 할 수 있습니다 . 애플리케이션이이를 합리적으로 예측하거나 미리 발생한 것을 감지 할 수있는 방법은 없습니다.

CPython은이를 더욱 복잡하게 만듭니다. 사용자 지정 메모리 할당 자 위에 사용자 지정 2 수준 개체 할당자가 malloc있습니다. ( 자세한 설명 은 소스 주석 을 참조하십시오 .) 또한 C API 수준에서도 훨씬 적은 Python에서도 최상위 수준 개체의 할당이 취소되는 시점을 직접 제어 할 수도 없습니다.

따라서 객체를 릴리즈 할 때 OS에 메모리를 릴리즈할지 여부를 어떻게 알 수 있습니까? 글쎄, 먼저 GC가 할당을 해제 할 수 있도록 마지막 참조 (알지 못한 내부 참조 포함)를 발표했음을 알아야합니다. (다른 구현과 달리, 적어도 CPython은 허용되는 즉시 객체를 할당 해제합니다.) 일반적으로 다음 레벨에서 적어도 두 가지를 할당 취소합니다 (예 : 문자열의 경우 PyString객체를 해제 하고 문자열 버퍼 ).

당신이 경우에 이 객체 저장의 블록 할당을 해제 다음 수준 아래로 발생 여부를 알고, 객체를 할당 해제, 당신은 그것을 구현 얼마나뿐만 아니라, 객체 할당의 내부 상태를 알아야합니다. (블록의 마지막 항목을 할당 해제하지 않으면 일어날 수 없으며, 심지어는 일어날 수 없습니다.)

당신이 경우 어떻게 이것이 원인인지 알고, 객체 저장의 블록을 할당 해제 free전화를, 당신은 그것을 구현 얼마나뿐만 아니라의 PyMem 할당의 내부 상태를 알아야합니다. (다시 말해서, malloced region 내에서 마지막 사용중인 블록을 할당 해제해야하며 , 그럴 경우에도 발생하지 않을 수 있습니다.)

에드 리전 을 수행 free 하는 경우 malloc이로 인해 munmap또는 이에 해당하는 (또는 brk) 원인이되는지 알기 위해서는의 내부 상태와 malloc구현 방법 을 알아야합니다 . 그리고 이것은 다른 플랫폼과 달리 플랫폼에 따라 다릅니다. (그리고 다시, 일반적으로 세그먼트 malloc내 에서 마지막 사용 중을 할당 해제해야하며 mmap, 그런 경우에도 발생하지 않을 수 있습니다.)

따라서 정확히 50.5MB가 릴리스 된 이유를 이해하려면 아래에서 위로 추적해야합니다. malloc하나 이상의 free호출을 할 때 왜 50.5mb에 해당하는 페이지를 매핑 해제 했 습니까? 플랫폼의을 읽고 malloc다양한 테이블과 목록을 걸어 현재 상태를 확인해야합니다. (일부 플랫폼에서는 시스템 수준 정보를 사용할 수도 있습니다. 시스템 수준의 스냅 샷을 만들지 않고는 오프라인으로 검사하지 않으면 캡처 할 수 없지만 운 좋게도 일반적으로 문제가되지는 않습니다.) 위의 3 단계에서 동일한 작업을 수행하십시오.

따라서이 질문에 대한 유용한 답변은 "왜냐"입니다.

리소스 제한 (예 : 임베디드) 개발을 수행하지 않는 한 이러한 세부 사항에 신경 쓸 이유가 없습니다.

당신이 만약 되는 자원이 제한된 개발하고, 이러한 세부 사항을 아는 것은 쓸모가 없다; 이러한 모든 수준과 특히 mmap응용 프로그램 수준에서 필요한 메모리를 중심으로 엔드 런을 수행 해야합니다 (간단하고 이해하기 쉬운 응용 프로그램 특정 영역 할당 자 사이에있을 수 있음).


2

먼저, 당신은 눈을 설치 할 수 있습니다 :

sudo apt-get install python-pip build-essential python-dev lm-sensors 
sudo pip install psutil logutils bottle batinfo https://bitbucket.org/gleb_zhulik/py3sensors/get/tip.tar.gz zeroconf netifaces pymdstat influxdb elasticsearch potsdb statsd pystache docker-py pysnmp pika py-cpuinfo bernhard
sudo pip install glances

그런 다음 터미널에서 실행하십시오!

glances

파이썬 코드에서 파일의 시작 부분에 다음을 추가하십시오.

import os
import gc # Garbage Collector

"Big"변수 (예 : myBigVar)를 사용한 후 메모리를 해제하려면 파이썬 코드에서 다음을 작성하십시오.

del myBigVar
gc.collect()

다른 터미널에서 파이썬 코드를 실행하고 "glances"터미널에서 시스템에서 메모리가 어떻게 관리되는지 관찰하십시오!

행운을 빕니다!

추신 : 나는 당신이 데비안 또는 우분투 시스템에서 일하고 있다고 가정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.