Python 쓰레기 수 거 는 어떻게 이 루어 집 니까?

쓰레기 수 거 가 뭐야?
쓰레기 수 거(GC)는 쓰레기 수 거 가 무엇 인지 많이 알 아야 합 니 다.쓰레기 수 거 GC 의 전체 조합 은 Garbage Collection 이다.위 키 백과 에 서 는 컴퓨터 과학 에서 쓰레기 수 거(영어:Garbage Collection,GC 로 약칭)는 자동 메모리 관리 체제 라 고 정의 한다.컴퓨터 의 동적 메모리 가 더 이상 필요 하지 않 을 때 메모리 를 내 보 내야 합 니 다.이러한 메모리 자원 관 리 를 쓰레기 회수 라 고 합 니 다.우 리 는 모두 C/C++에서 사용자 가 스스로 메모 리 를 관리 하고 유지 해 야 한 다 는 것 을 알 고 있 습 니 다.스스로 메모 리 를 관리 하 는 것 은 매우 자 유 롭 고 마음대로 메모 리 를 신청 하고 방출 할 수 있 지만 메모리 유출,공중 지침 등 문제 가 발생 하기 쉽 습 니 다.현재 의 고급 언어 인 자바,Python 등 은 모두 쓰레기 회수 체 제 를 사용 하여 자동 으로 메모리 관 리 를 하고 쓰레기 회수 체 제 는 두 가지 일 에 전념 합 니 다.① 메모리 에 쓸모없는 쓰레기 자원 을 찾 습 니 다.② 이 쓰레기 자원 을 제거 하고 메모 리 를 다른 대상 에 게 사용 하도록 한다.
Python 은 해석 형 언어 로 서 간단 하고 알 기 쉬 운 문법 때문에 우 리 는 변수의 유형 을 설명 할 필요 가 없습니다.변수 유형의 확정,메모리 공간의 분배 와 방출 은 모두 Python 해석 기 가 실 행 될 때 자동 으로 진행 되 므 로 우 리 는 관심 을 가 질 필요 가 없습니다.Python 이라는 자동 메모리 관리 기능 은 개발 자의 인 코딩 부담 을 크게 줄 이 고 개발 자 들 로 하여 금 업무 실현 에 전념 하 게 하 는 것 도 Python 자체 의 중요 한 원인 중 하나 입 니 다.다음은 파 이 썬 의 메모리 관 리 를 살 펴 보 겠 습 니 다.
Python 의 쓰레기 회수 메커니즘
인용 계수
Python 에서 모든 것 이 대상 입 니 다.즉,Python 에서 사용 하 는 모든 변 수 는 본질 적 으로 클래스 대상 입 니 다.실제로 모든 대상 의 핵심 은 하나의**"구조 체 PyObject"*입 니 다.그 내부 에는 인용 카운터 ob 가 있 습 니 다.refcnt,프로그램 이 실행 되 는 과정 에서 실시 간 으로 ob 를 업데이트 합 니 다.refcnt 의 값 은 현재 대상 의 이름 을 참조 하 는 수량 을 반영 합 니 다.어떤 대상 의 인용 계수 가 0 이면 이 대상 이 쓰레기 가 되 었 다 는 것 을 설명 합 니 다.그러면 회수 되 고 사용 하 는 메모리 도 즉시 방출 됩 니 다.

typedef struct _object {
    int ob_refcnt;//    
    struct _typeobject *ob_type;
} PyObject;

아래 의 상황 은 인용 계수 에 1 을 더 하 는 상황 을 초래 합 니 다.
① 대상 생 성,예 를 들 어 a=5
② 대상 인용,b=a
③ 대상 은 매개 변수 로 한 함수 에 전 달 됩 니 다.(주의해 야 할 것 은 함수 호출 이 발생 할 때 추가 적 인 두 번 의 인용 이 발생 합 니 다.한 번 은 함수 스 택 에서 오고 다른 하 나 는 함수 매개 변수 입 니 다)
④ 대상 은 하나의 요소 로 용기 에 저 장 됩 니 다(예 를 들 어 목록 에 저 장 됩 니 다)
아래 의 상황 은 인용 수 를 1 로 줄 일 수 있 습 니 다.
① 대상 별명 이 사라 짐 del a
② 대상 별명 에 새로운 대상 부여
③ 대상 이 역할 영역 에서 벗 어 나 는 경우
④ 대상 이 있 는 용기 가 소각 되 거나 용기 에서 삭제 되 는 경우
또한 sys 패키지 의 getrefcount()를 통 해 인용 대상 의 현재 인용 계 수 를 가 져 올 수 있 습 니 다.(여기 getrefcount()자체 가 인용 계 수 를 1 로 추가 할 수 있 습 니 다.)

import sys
a = [1, 2, 3]
print(sys.getrefcount(a))
#    2,       (    a   ,    getrefcount)

def func(a):
    print(sys.getrefcount(a))
    #    4,       (a   、Python      ,    , getrefcount)

func(a)
print(sys.getrefcount(a))
#    2,       (    a   ,    getrefcount),    func       

다음은 메모리 사용 각도 에서 보 겠 습 니 다.

import os
import psutil


def show_memory_info(hint):
    """
         python          
    :param hint:
    :return:
    """
    pid = os.getpid()
    p = psutil.Process(pid)

    info = p.memory_full_info()
    memory = info.rss / 1024 / 1024
    print('{}          : {} MB'.format(hint, memory))


def func():
    show_memory_info('  ')
    a = [i for i in range(9999999)]
    show_memory_info('  a  ')


func()
show_memory_info('  ')

출력 은 다음 과 같 습 니 다:
현재 프로 세 스 의 초기 메모리 사용:12.125 MB
a 를 만 든 후 현재 프로 세 스 의 메모리 사용:205.15625 MB
현재 프로 세 스 의 메모리 사용 종료:12.87890625 MB
이 를 통 해 알 수 있 듯 이 현재 프로 세 스 의 초기 메모리 사용 은 12.125 MB 이 고 함수 func()가 목록 a 를 만 든 후에 메모리 사용량 은 205.15625 MB 로 급 격 히 증가 하 였 으 며 함수 호출 이 끝 난 후에 메모리 사용량 은 정상 으로 되 돌 아 왔 다.이것 은 함수 내부 성명 의 목록 a 는 부분 변수 로 함수 가 돌아 온 후에 부분 변수의 인용 이 취소 되 기 때 문 입 니 다.이때 목록 a 가 가리 키 는 대상 의 인용 수 는 0 이 고 Python 은 쓰레기 수 거 를 실행 하기 때문에 이전에 사용 한 대량의 메모리 가 다시 돌 아 왔 습 니 다.
반복 참조
순환 참조 란 무엇 입 니까?쉽게 말 하면 두 대상 이 서로 인용 하 는 것 이다.다음 절 차 를 보십시오.

def func2():
    show_memory_info('  ')
    a = [i for i in range(10000000)]
    b = [x for x in range(10000001, 20000000)]
    a.append(b)
    b.append(a)
    show_memory_info('  a,b  ')

func2()
show_memory_info('  ')

출력 은 다음 과 같 습 니 다:
현재 프로 세 스 의 초기 메모리 사용:12.14453125 MB
a,b 를 만 든 후 현재 프로 세 스 의 메모리 사용:396.6875 MB
현재 프로 세 스 의 메모리 사용 종료:396.96875 MB
이 를 통 해 알 수 있 듯 이 프로그램 에서 a 와 b 는 서로 인용 되 고 부분 변수 로 함수 func 2 호출 이 끝 난 후에 a 와 b 는 프로그램의 의미 에서 존재 하지 않 지만 출력 결과 에서 볼 때 아직도 메모리 사용량 이 있 습 니 다.이것 은 왜 일 까요?서로 인용 하기 때문에 인용 수가 0 이 아니다.
만약 에 생산 환경 에서 순환 인용 이 나타 나 고 다른 쓰레기 회수 체제 가 없 는 상황 에서 장시간 운행 한 후에 프로그램 이 사용 하 는 메모리 가 점점 커 질 것 이다.만약 에 제때에 처리 되 지 않 으 면 반드시 서버 를 가득 채 울 것 이다.
순환 인용 을 사용 하지 않 으 면 gc.collect()를 명시 적 으로 호출 하여 쓰레기 수 거 를 시작 할 수 있 습 니 다.

def func2():
    show_memory_info('  ')
    a = [i for i in range(10000000)]
    b = [x for x in range(10000001, 20000000)]
    a.append(b)
    b.append(a)
    show_memory_info('  a,b  ')

func2()
gc.collect()
show_memory_info('  ')

출력 은 다음 과 같 습 니 다:
현재 프로 세 스 의 초기 메모리 사용:12.29297575 MB
a,b 를 만 든 후 현재 프로 세 스 의 메모리 사용:396.69140625 MB
현재 프로 세 스 의 메모리 사용 종료:12.95703125 MB
인용 계수 메커니즘 은 효율 적 이 고 간단 하 며 실시 간성(일단 0 이 되면 바로 해 버린다)등 장점 이 있 는데 한 대상 의 인용 계수 가 0 이 되면 메모리 가 바로 방출 된다.다른 메커니즘 처럼 특정 시 기 를 기다 릴 필요 가 없다.쓰레기 수 거 를 무 작위 로 운행 단계 에 배정 하고 회수 메모 리 를 처리 하 는 시간 을 평소 로 나 누 어 정상 적 인 프로그램의 운행 이 비교적 평온 하 다.그러나 인용 계수 에 도 단점 이 있다.일반적인 단점 은 다음 과 같다.
① 논 리 는 간단 하지만 유지 하기 가 어렵다.모든 대상 은 인용 계 수 를 통계 하기 위해 별도의 공간 을 분배 하고 인용 계 수 를 유지 해 야 한다.이것 은 자원 을 소모 해 야 한다.
② 반복 참조.이 는 계수 체 제 를 인용 한 치명상 이 며,인용 수 는 풀 리 지 않 기 때문에 다른 쓰레기 회수 알고리즘 을 사용 해 보충 해 야 한다.
사실 Python 은 태그 제거(mark-sweep)알고리즘 과 세대 별 수집(geneational)을 사용 하여 순환 참조 에 대한 자동 쓰레기 수 거 를 사용 합 니 다.
태그 지우 기 순환 참조 해제
Python 은 태그-제거(Mark and Sweet)알고리즘 을 사용 하여 용기 대상 이 발생 할 수 있 는 순환 참조 문 제 를 해결 합 니 다.(목록,사전,사용자 정의 클래스 의 대상,모듈 등 용기 클래스 대상 만 순환 참조 가 발생 할 수 있 습 니 다.숫자 와 같이 문자열 과 같은 간단 한 유형 은 순환 참조 가 나타 나 지 않 습 니 다.최적화 전략 으로서 간단 한 유형 만 포함 하 는 원 그룹 에 대해 서도 태그 제거 알고리즘 을 고려 하지 않 습 니 다)
이 단 계 는 두 단계 로 나 뉜 다.첫 번 째 단 계 는 태그 단계 이 고 GC 는 모든 활동 대상 을 표시 하고 두 번 째 단 계 는 표시 되 지 않 은 비 활동 대상 을 회수 하 는 것 이다.
그렇다면 파 이 썬 은 어떤 대상 을 비 활동 대상 으로 판단 할 것 인가?
모든 대상 의 집합 에 대해 우 리 는 먼저 인용 계수 사본 표를 만들어 그들의 인용 수 를 저장 한 다음 에 집합 내부 의 인용 을 모두 해제 한다(내부 인용 은 이 집합 중의 한 대상 이 본 집합 내부 의 다른 대상 을 인용 한 것 을 말한다).해제 하 는 과정 에서 사본 표 에서 인용 수 를 줄 이 고 모든 내부 인용 을 해제 한 후에복사 본 표 의 인용 수 는 여전히 0 이 아니 라 뿌리 집합 이다.그 다음 에 표기 과정 을 시작한다.즉,집합 노드 와 점차적으로 인용 을 회복 하고 복사 본 표 의 인용 수 를 늘린다.마지막 으로 복사 본 표 에서 인용 계수 가 0 인 것 이 바로 쓰레기 대상 이다.우 리 는 그것들 을 쓰레기 로 회수 해 야 한다.예 를 들 면:

위의 이 집합 중의 노드 는 외부 에서 들 어 오 는 연결(a 와 b 까지)도 있 고 외부 로 의 연결(c 는 바깥 의 특정한 대상 을 참조)도 있 습 니 다.오른쪽 은 계수 표를 참조 한 다음 에 우 리 는 모든 내부 연결 을 뜯 습 니 다.

그러면 뿌리 집합 은 a 와 b 입 니 다.그리고 우 리 는 a 와 b 에서 출발 하여 인용 수 를 표시 하고 복원 합 니 다.

a 와 b 에서 출발 하여 도달 할 수 있 는 노드 가 모두 회복 되 었 습 니 다.인용 수 는 0 입 니까?바로 이 집합 내부 에서 순환 적 으로 인용 한 쓰레기(e 와 f)입 니 다.모든 대상 을 하나의 집합 으로 보면 모든 쓰레기 를 회수 할 수 있 고 모든 대상 을 하나의 작은 집합 으로 나 누 어 각각 작은 집합 안의 쓰레기 를 회수 할 수 있 습 니 다.
하지만 매번 그림 을 옮 겨 다 니 는 것 은 파 이 썬 에 게 는 엄 청 난 성능 낭비 다.
분할 회수
세대 별 수 거 는 메모리 가 대상 의 생존 시간 에 따라 다른 집합 으로 구분 되 는 공간 으로 시간 을 바 꾸 는 조작 방식 으로,파 이 썬 은 메모리 가 젊 은 세대(0 세대),중 연대(1 세대),노후(2 세대)로 각각 3 세대 로 구분 된다.이들 은 3 개의 링크 에 대응 하여 쓰레기 수집 빈 도 는 대상 의 생존 시간 이 커지 면서 줄어든다.
새로 생 성 된 대상 은 모두 젊 은 세대 에 배정 된다.젊 은 세대 링크 의 총수 가 상한 선 에 이 르 렀 을 때 쓰레기 회수 기 에 추 가 된 대상 에서 삭제 대상 을 제외 하고 해당 하 는 한도 에 이 르 렀 을 때 이 세대 대상 에 게 쓰레기 수 거 를 시작 하여 회수 할 수 있 는 대상 을 회수 하고 회수 하지 않 는 대상 은 중 년대 로 옮 겨 간다.이런 식 으로 유추 하면옛날 시대 의 대상 은 생존 시간 이 가장 긴 대상 이 었 고 심지어 전체 시스템 의 생명 주기 에 생존 했다.또한 세대 별 회 수 는 태그 제거 기술 을 바탕 으로 한다.사실 세대 별 수 거 는 신입생 의 대상 이 쓰레기 수 거 될 가능성 이 높 고 더 오래 살아 남 은 대상 도 더 높 은 확률 로 살아 남 을 수 있다 는 사상 을 바탕 으로 한다.따라서 이 를 통 해 많은 계 산 량 을 절약 하고 파 이 썬 의 성능 을 향상 시 킬 수 있다.
총결산
쓰레기 수 거 는 파 이 썬 이 자체 적 으로 가지 고 있 는 메커니즘 으로 더 이상 사용 되 지 않 는 메모리 공간 을 자동 으로 방출 하 는 데 사용 되 며,파 이 썬 에 서 는 주로 인용 수 를 통 해 쓰레기 수 거 를 하고,표 시 를 통 해 용기 대상 이 발생 할 수 있 는 순환 인용 문 제 를 제거 하고,세대 별 수 거 를 통 해 공간 을 바 꾸 는 방법 으로 쓰레기 수 거 효율 을 높 인 다.
파 이 썬 쓰레기 수 거 가 어떻게 이 루어 졌 는 지 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.파 이 썬 쓰레기 수 거 에 관 한 더 많은 내용 은 예전 의 글 을 검색 하거나 아래 의 관련 글 을 계속 찾 아 보 세 요.앞으로 많은 응원 부 탁 드 리 겠 습 니 다!

좋은 웹페이지 즐겨찾기