본문 바로가기
DEVLOG/개발일기

[메모리 관리] 파이썬(Python)에서 메모리 관리하기

2019. 9. 30.
반응형

개발자로서 메모리 관리에 대한 이해는 중요합니다. 효율적으로 파이썬 코드를 작성한다는 것은 메모리 효율적인 코드 작성을 의미합니다. 빅데이터의 사용이 증가함에 따라 메모리 관리의 중요성을 간과할 수 없습니다. 비효율적인 메모리 관리로 인해 응용프로그램 및 서버 구성요소가 느려지기도 합니다. 메모리 누수(memory leak)는 종종 테스트 및 디버깅에 많은 시간을 소비합니다. 또한 데이터 처리에 혼란을 초래하고 동시 처리 문제를 일으킬 수 있습니다.

 

파이썬의 메모리 관리는 대부분 Python Memory Manager에 의해 수행되지만 최상의 코딩 방법과 Python Memory Manager 작동 방식에 대한 이해는 보다 효율적이고 유지 관리 가능한 코드로 이어질 수 있습니다.


 

개발자를위한 메모리 관리에서 가장 중요한 부분은 메모리 할당(memory allocation)입니다. 컴퓨터의 실제 또는 가상 메모리에 빈 공간 블록을 할당하는 프로세스를 이해하는 것이 중요합니다. 메모리 할당에는 두 가지 유형이 있습니다.


정적 메모리 할당 — 프로그램 컴파일시 메모리가 할당됩니다. 이에 대한 예는 C/C ++에 있으며 고정 크기로만 정적 배열을 선언합니다. 메모리는 컴파일 할 때 할당됩니다. 스택은 정적 할당을 구현하는 데 사용됩니다. 이 경우 메모리를 재사용 할 수 없습니다.

static int a=10;

 

동적 메모리 할당 — 프로그램은 런타임에 메모리가 할당됩니다. 이에 대한 예는 C / C ++에 있으며, 단항 연산자 new를 사용하여 배열을 선언합니다. 메모리는 런타임에 할당됩니다. 힙은 동적 할당을 구현하는 데 사용됩니다. 이 경우 필요하지 않은 메모리를 비우고 재사용 할 수 있습니다.

int *p;
p = new int;

 

파이썬의 좋은 점은 파이썬의 모든 것이 객체라는 것입니다. 이것은 동적 메모리 할당이 파이썬 메모리 관리의 기초라는 것을 의미합니다. 객체가 더 이상 필요하지 않으면 Python 메모리 관리자가 자동으로 객체에서 메모리를 회수합니다.


 

Python은 C 프로그래밍 언어로 구현 된 고급 프로그래밍 언어입니다. Python 메모리 관리자는 Python의 메모리 할당을 관리합니다. 모든 파이썬 객체와 데이터 구조를 포함하는 개인 힙이 있습니다. Python 메모리 관리자는 요청시 Python 힙을 관리합니다. Python 메모리 관리자에는 객체 별 할당자가있어 int, string 등과 같은 특정 객체에 대해 메모리를 명확하게 할당 할 수 있습니다. 그 아래에서 원시 메모리 할당자는 운영 체제의 메모리 관리자와 상호 작용하여 개인 힙에 공간이 있는지 확인합니다.

 

Python 메모리 관리자는 "블록"이라는 메모리 청크를 관리합니다. 동일한 크기의 블록 모음이 "풀"을 구성합니다. 풀은 힙 = 64 풀에 할당 된 256kB 메모리 덩어리 인 Arena에서 생성됩니다. 객체가 파손되면 메모리 관리자는이 공간을 동일한 크기의 새 객체로 채 웁니다.

 

메소드와 변수는 스택 메모리에 작성됩니다. 메소드와 변수가 작성 될 때마다 스택 프레임이 작성됩니다. 이러한 프레임은 메소드가 리턴 될 때마다 자동으로 제거됩니다.

 

오브젝트 및 인스턴스 변수는 힙 메모리에 작성됩니다. 변수와 함수가 반환 되 자마자 죽은 개체는 가비지 수집됩니다.

 

Python 메모리 관리자가 반드시 메모리를 운영 체제로 다시 릴리스 할 필요는 없으며 대신 메모리가 Python 인터프리터로 다시 리턴됩니다. 파이썬에는 작은 객체 할당자가있어 추후 사용을 위해 메모리를 할당합니다. 장기 실행 프로세스에서 사용되지 않는 메모리의 증분 예약이있을 수 있습니다.


 

효율적인 파이썬 코드를위한 모범 사례

리스트에 원소를 추가할 때 join 하기

line1, line2를 mymsg에 개별적으로 추가하는 대신 list 및 join을 사용하십시오.
이렇게하지 마십시오

mymsg=’line1\n’
mymsg+=’line2\n’

더 나은 선택 :

mymsg=[‘line1’,’line2’]
‘\n’.join(mymsg)

 

문자열에 + 연산자 피하기

피할 수 있으면 연결에 + 연산자를 사용하지 마십시오. 문자열은 변경할 수 없으므로 문자열에 요소를 추가 할 때마다 Python은 새 문자열과 새 주소를 만듭니다. 이는 문자열이 변경 될 때마다 새 메모리를 할당해야 함을 의미합니다.

이렇게 하지 마십시오

msg = ’hello’ + mymsg + ’world’

더 나은 방법

msg=’hello %s world’ % mymsg

 

제너레이터(Generator) 사용하기

생성기를 사용하면 한 번에 모든 항목이 아닌 한 번에 하나의 항목을 반환하는 함수를 만들 수 있습니다. 즉, 데이터 집합이 큰 경우 전체 데이터 집합에 액세스 할 때까지 기다릴 필요가 없습니다.

def __iter__(self):
    return self._generator()
    
def _generator(self):
    for itm in self.items():
        yield itm

 

루프 밖에서 평가하기

데이터를 반복하는 경우 캐시 된 버전의 정규식을 사용할 수 있습니다.

match_regex=re.compile(“foo|bar”)
for i in big_it:
    m = match_regex.search(i)
        ….

 

지역 변수에 함수 할당하기

파이썬은 전역 변수보다 훨씬 효율적으로 지역 변수에 액세스합니다. 지역 변수에 함수를 할당 한 다음 사용하십시오.

myLocalFunc=myObj.func
for i in range(n):
    myLocalFunc(i)

 

내장 함수와 라이브러리 사용하기

가능하면 내장 함수와 라이브러리를 사용하십시오. 내장 함수는 종종 최상의 메모리 사용 사례를 사용하여 구현됩니다.

하지마세요

mylist=[]
for myword in oldlist:
    mylist.append(myword.upper())

더 나은 방법

mylist = map(str.lower, oldlist)

 

루프보다 키워드 인수를 사용하여 데이터 세트를 작성하는 것이 더 좋습니다.

mycounter = Counter (a = 1, b = 2, c = 3, d = 5, e = 6, f = 7, g = 8)
for i in mycounter.elements():

 

itertools를 사용하여 원치 않는 루프 제거

itertools는 루프에서 많은 시간을 절약합니다. 또한 코드의 복잡성을 제거합니다.

하지마세요

mylist=[]
for shape in [True, False]:
    for weight in (1, 5):
        firstlist=firstlist+function(shape, weight)

더 나은 방법

from itertools import product, chain
list(chain.from_iterable(function(shape, weight) for weight, shape in product([True, False], range(1, 5))))

 

안전 및 메모리 관리를 위해 _new_ 덮어 쓰기 및 메타 클래스 활용

__new__를 덮어 쓰고 메타 클래스를 활용하면 Singleton 및 Flyweight 패턴을 적용 할 때 메모리 관리에 유용하고 안전합니다. 예를 들어 다음은 Yaml 파일을 읽는 dict 객체의 예입니다. 메타 클래스는 일단 정의되면 싱글 톤 디자인 패턴이므로 시스템의 어느 곳으로나 가져 와서 다시 정의 할 수 있으며 인터프리터는 초기 객체 만 가리 킵니다. 메모리 공간을 줄이고 안전을 보장합니다. 팀의 다른 개발자가 아무리 주니어에 있더라도 중복 된 객체는 발생하지 않으므로 시스템의 한 부분에서 dict를 변경하지 못하게하고 다른 부분에서 다른 dict를 참조하지 않습니다.

class Singleton(type):
 
    _instances = {}
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
            return cls._instances[cls]

class ConfigDict(dict, metaclass=Singleton):
    def __init__(self):
        super().__init__(self.read_config_file())
        
    @staticmethod
    def read_config_file():
    “””
    Reads config file based on path passed when running app.
    
    :return: (dict) loaded data from yml file
    “””
    
    config_file_path = sys.argv[-1]
    if not config_file_path.endswith(“.yml”):
        raise ConfigDictError(message=”yml file not passed into flask app but {} instead”.format(config_file_path))
        return yaml.load(open(str(config_file_path)), Loader=yaml.FullLoader)

 

파이썬 코드에서 성능을 확인하는 방법

성능 점검을 위해 cProfile 및 프로파일과 같은 프로파일 링 모듈을 사용할 수 있습니다.

python -m cProfile [-o output_file][-s sort_order](-m module | myscript.py)

문자열을 뒤집는 가장 좋은 방법을 확인하려면 벤치마킹의 전체 프로세스를 실행하는이 기사를 확인하십시오.


 

반응형

댓글