Garbage Collection 수집 주기
전통적으로 PHP에서 이전에 사용했던 것과 같은 참조 카운팅 메모리 메커니즘은 순환 참조 메모리 누수를 해결하지 못합니다. 그러나 5.3.0부터 PHP는 해당 문제를 해결하는 » 참조 계산 시스템의 동시 주기 수집 문서에서 동기 알고리즘을 구현합니다.
알고리즘 작동 방식에 대한 전체 설명은 이 섹션의 범위를 약간 벗어나지만 기본 사항은 여기에서 설명합니다. 우선 몇 가지 기본 규칙을 설정해야 합니다. refcount가 증가하면 여전히 사용 중이므로 쓰레기가 아닙니다. refcount가 감소하고 0에 도달하면 zval이 해제될 수 있습니다. 이것은 refcount 인수가 0이 아닌 값으로 감소될 때만 가비지 사이클이 생성될 수 있음을 의미합니다. 두 번째로, 가비지 사이클에서 refcount를 1로 줄이는 것이 가능한지 여부를 확인하고 어떤 부분이 refcount가 0인지 확인하여 어떤 부분이 가비지인지 알아낼 수 있습니다.
refcount가 감소할 때마다 가비지 주기 검사를 호출해야 하는 것을 피하기 위해 알고리즘은 대신 가능한 모든 루트(zval)를 "루트 버퍼"에 넣습니다("보라색"으로 표시). 또한 가능한 각 가비지 루트가 버퍼에서 한 번만 종료되도록 합니다. 루트 버퍼가 가득 찼을 때만 내부의 모든 다른 zval에 대해 수집 메커니즘이 시작됩니다. 위 그림의 A 단계를 참조하십시오.
B 단계에서 알고리즘은 가능한 모든 루트에 대해 깊이 우선 검색을 실행하여 찾은 각 zval의 refcounts를 하나씩 감소시키고 동일한 zval에 대한 refcount를 두 번("회색"으로 표시하여) 감소시키지 않도록 합니다. C 단계에서 알고리즘은 각 루트 노드에서 깊이 우선 검색을 다시 실행하여 각 zval의 참조 수를 다시 확인합니다. refcount가 0인 경우 zval은 "흰색"(그림에서 파란색)으로 표시됩니다. 0보다 크면 해당 지점부터 깊이 우선 검색을 사용하여 refcount를 1만큼 감소시키고 다시 "검정색"으로 표시합니다. 마지막 단계(D)에서 알고리즘은 루트 버퍼에서 zval 루트를 제거하고 이전 단계에서 "흰색"으로 표시된 zval을 확인합니다. "흰색"으로 표시된 모든 zval은 해제됩니다.
알고리즘이 어떻게 작동하는지 기본적으로 이해했으므로 이것이 PHP와 어떻게 통합되는지 다시 살펴보겠습니다. 기본적으로 PHP의 가비지 수집기는 켜져 있습니다. 그러나 이것을 변경할 수 있는 php.ini 설정이 있습니다: zend.enable_gc.
가비지 수집기가 켜져 있으면 루트 버퍼가 가득 찰 때마다 위에서 설명한 주기 찾기 알고리즘이 실행됩니다. 루트 버퍼의 고정 크기는 가능한 루트 10,000개입니다(PHP 소스 코드의 Zend/zend_gc.c
에서 GC_ROOT_BUFFER_MAX_ENTRIES
상수를 변경하고 PHP를 다시 컴파일하여 변경할 수 있음). 가비지 수집기가 꺼지면 주기 찾기 알고리즘이 실행되지 않습니다. 그러나 이 구성 설정으로 가비지 수집 메커니즘이 활성화되었는지 여부에 관계없이 가능한 루트는 항상 루트 버퍼에 기록됩니다.
가비지 수집 메커니즘이 꺼져 있는 동안 루트 버퍼가 가능한 루트로 가득 차면 더 이상 가능한 루트가 기록되지 않습니다. 기록되지 않은 가능한 루트는 알고리즘에 의해 분석되지 않습니다. 순환 참조 주기의 일부인 경우 정리되지 않고 메모리 누수가 발생합니다.
메커니즘이 비활성화되어 있어도 가능한 루트가 기록되는 이유는 가능한 루트를 찾을 수 있을 때마다 메커니즘이 켜져 있는지 확인해야 하는 것보다 가능한 루트를 기록하는 것이 더 빠르기 때문입니다. 그러나 가비지 수집 및 분석 메커니즘 자체에는 상당한 시간이 소요될 수 있습니다.
zend.enable_gc 구성 설정을 변경하는 것 외에도 gc_enable() 또는 gc_disable()을 각각 호출하여 가비지 수집 메커니즘을 켜고 끌 수도 있습니다. 이러한 기능을 호출하면 구성 설정으로 메커니즘을 켜거나 끄는 것과 같은 효과가 있습니다. 가능한 루트 버퍼가 아직 가득 차지 않은 경우에도 주기 수집을 강제할 수도 있습니다. 이를 위해 gc_collect_cycles() 함수를 사용할 수 있습니다. 이 함수는 알고리즘에 의해 수집된 사이클 수를 반환합니다.
메커니즘을 켜고 끄고 직접 주기 수집을 시작하는 기능 뒤에 있는 근거는 응용 프로그램의 일부가 시간에 매우 민감할 수 있다는 것입니다. 이러한 경우에는 가비지 수집 메커니즘이 시작되는 것을 원하지 않을 수 있습니다. 물론 응용 프로그램의 특정 부분에 대한 가비지 수집을 끄면 일부 가능한 루트가 제한된 루트 버퍼에 맞지 않을 수 있기 때문에 메모리 누수가 발생할 위험이 있습니다. . 따라서 루트 버퍼에 이미 기록된 가능한 루트를 통해 손실될 수 있는 메모리를 해제하기 위해 gc_disable()을 호출하기 직전에 gc_collect_cycles()를 호출하는 것이 현명할 것입니다. 그러면 사이클 수집 메커니즘이 꺼져 있는 동안 가능한 루트를 저장할 수 있는 더 많은 공간이 있도록 빈 버퍼가 남습니다.