재활용은 성능에 약간의 영향을 미칠 수 있지만 이는 PHP 5.2와 PHP 5.3을 비교할 때만 해당됩니다. PHP 5.2에서는 로깅이 전혀 로깅하지 않는 것보다 느릴 수 있지만 PHP 5.3의 PHP 런타임에 대한 다른 변경 사항은 이러한 성능 저하를 줄입니다.
성능에 영향을 미치는 두 가지 주요 영역이 있습니다. 첫 번째는 메모리 공간 절약이고, 다른 하나는 가비지 컬렉션 메커니즘이 메모리 정리를 수행할 때 실행 시간(런타임 지연)이 증가한다는 것입니다. 우리는 두 영역을 모두 살펴보겠습니다.
메모리 공간 절약
우선, 가비지 수집 메커니즘을 구현하는 전체 이유는 전제 조건이 충족되면 순환 참조 변수를 정리하여 메모리 공간을 절약하는 것입니다. PHP 실행 시 루트 버퍼가 가득 차거나 gc_collect_cycles() 함수가 호출되면 가비지 수집이 수행됩니다. 아래 그림에는 PHP 5.2 및 PHP 5.3 환경에서 다음 스크립트의 메모리 사용량이 표시되어 있으며, 스크립트 시작 시 PHP 자체가 차지하는 기본 메모리는 제외됩니다.
예제 #1 메모리 사용 예
<?php class Foo { public $var = '3.1415962654'; } $baseMemory = memory_get_usage(); for ( $i = 0; $i <= 100000; $i++ ) { $a = new Foo; $a->self = $a; if ( $i % 500 === 0 ) { echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "\n"; } } ?>
이 이론적인 예에서는 다음과 같은 개체를 만듭니다. 획득하고 이 개체의 속성은 개체 자체를 다시 참조하도록 설정됩니다. 일반적인 메모리 누수는 루프의 다음 반복 중에 스크립트의 변수가 다시 복사될 때 발생합니다. 이 예에서는 두 개의 변수 컨테이너(객체 컨테이너와 속성 컨테이너)가 유출되었지만 가능한 루트는 하나만 찾을 수 있습니다. 즉, 설정되지 않은 변수입니다. 10,000회 반복(결과적으로 총 10,000개의 루트 가능) 후 루트 버퍼가 가득 차면 가비지 수집 메커니즘이 수행되고 가능한 루트와 관련된 메모리가 해제됩니다. 이는 PHP 5.3의 톱니형 메모리 사용량 그래프에서 쉽게 확인할 수 있습니다. 10,000회 반복 실행 후 가비지 수집이 수행되고 관련 재사용 참조 변수가 해제됩니다. 이 예에서는 유출된 데이터 구조가 매우 간단하므로 가비지 수집 메커니즘 자체는 많은 작업을 수행할 필요가 없습니다. 이 차트에서 PHP 5.3의 최대 메모리 공간은 약 9Mb인 반면, PHP 5.2의 메모리 공간은 계속 증가하고 있음을 알 수 있습니다.
런타임 저하
가비지 수집이 성능에 영향을 미치는 두 번째 영역은 누수된 메모리를 해제하는 데 걸리는 시간입니다. 시간이 얼마나 걸리는지 확인하기 위해 위 스크립트를 약간 변경하여 반복 횟수를 늘리고 루프에서 메모리 사용량 계산을 제거했습니다. 두 번째 스크립트 코드는 다음과 같습니다.
예제 #2 GC 성능에 영향
<?php class Foo { public $var = '3.1415962654'; } for ( $i = 0; $i <= 1000000; $i++ ) { $a = new Foo; $a->self = $a; } echo memory_get_peak_usage(), "\n"; ?>
이 스크립트를 두 번 실행합니다. 한 번은 zend.enable_gc를 구성하여 가비지 수집을 켤 때, 한 번은 끌 것입니다.
예제 #3 위 스크립트 실행
time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php # and time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php
내 컴퓨터에서 첫 번째 명령은 약 10.7초 동안 지속되었고 두 번째 명령은 11.4초가 걸렸습니다. 시간이 7% 증가했습니다. 그러나 이 스크립트를 실행할 때 최대 메모리 사용량은 931Mb에서 10Mb로 98% 감소했습니다. 이 벤치마크는 그다지 과학적이거나 실제 애플리케이션 데이터를 대표하지는 않지만 메모리 사용량 측면에서 가비지 수집의 이점을 보여줍니다. 좋은 소식은 이 스크립트의 경우 실행 시 더 많은 순환 참조 변수가 나타날 때 메모리 절약이 더 커지고 시간 증가율이 매번 7%라는 것입니다.
PHP 내부 GC 통계
PHP 내부에서는 가비지 수집 메커니즘이 작동하는 방식에 대한 자세한 정보를 표시할 수 있습니다. 하지만 이 정보를 표시하려면 먼저 PHP를 다시 컴파일하여 벤치마크 및 데이터 수집 코드를 사용할 수 있도록 해야 합니다. 원하는 대로 ./configure를 실행하기 전에 환경 변수 CFLAGS를 -DGC_BENCH=1로 설정해야 합니다. 다음 명령 문자열이 바로 그 작업을 수행합니다.
예제 #4 PHP를 다시 컴파일하여 GC 벤치마킹 활성화
export CFLAGS=-DGC_BENCH=1 ./config.nice make clean make
새로 컴파일된 PHP 바이너리를 사용하여 위의 예제 코드를 다시 실행하면 PHP 이후 실행이 끝나면 다음 정보를 볼 수 있습니다.
예제 #5 GC 통계
GC Statistics ------------- Runs: 110 Collected: 2072204 Root buffer length: 0 Root buffer peak: 10000 Possible Remove from Marked Root Buffered buffer grey -------- -------- ----------- ------ ZVAL 7175487 1491291 1241690 3611871 ZOBJ 28506264 1527980 677581 1025731
주요 정보 통계는 첫 번째 블록에 있습니다. 가비지 수집 메커니즘이 110번 실행되었으며 이 110번의 실행에서 총 2백만 개가 넘는 메모리 할당이 해제되었음을 알 수 있습니다. 가비지 수집 메커니즘이 한 번 이상 실행되는 한 루트 버퍼 피크(Root buffer peak)는 항상 10000입니다.
결론
보통 PHP의 가비지 수집 메커니즘은 다음과 같은 경우에만 작동합니다. 주기 수집 알고리즘은 런타임 동안 시간 소모가 증가합니다. 그러나 일반(더 작은) 스크립트에서는 성능에 전혀 영향을 미치지 않아야 합니다.
그러나 재활용 메커니즘이 실행되는 일반 스크립트의 경우 메모리 절약을 통해 서버에서 더 많은 스크립트를 동시에 실행할 수 있습니다. 사용된 총 메모리가 상한에 도달하지 않았기 때문입니다.
이러한 이점은 장기 실행 테스트 모음이나 데몬 스크립트와 같은 장기 실행 스크립트에서 특히 두드러집니다. 동시에 일반적으로 웹 스크립트보다 오래 실행되는 PHP-GTK 애플리케이션의 경우 새로운 가비지 수집 메커니즘은 메모리 누수를 해결하기 어렵다는 오랜 견해를 크게 바꿔야 합니다.