전 포스팅에 이어 이제 실제 Memory leak, gc 에 관해 알아보자
먼저 garbage collection이 무엇인지 정의하고 시작하자. Garbage collection은 object
의 메모리 사용을 추적하고, 또는 현재 범위에서 도달하지 못하는..그리고 프로그래머에 의해 사용되고 있는 메모리를 추적하는 메커니즘이다. 지난 포스팅에서 봤듯이 메모리해제는 zvals
의 refcount
가 0이될 때 해제되는 동일한 메커니즘이다.
PHP는 5.3버전 부터 등장하기 시작했다. Zend Grabage Collector or ZendGC 는 메모리를 자동으로 해제할 뿐더러 순환 참조(circular references)도 처리한다. Garbage Collection 참조
순환 참조는 objects
나 array
와 같은 혼합 타입에서 나타난다. 2개의 혼합타입이 서로를 참조할때, 또는 자기자신을 참조할 때를 순환참조라 한다. 순환참조는 메모리해제를 하기 어려운점이 있어 좋지않다.
PHP 5.3 이전에는 순환참조를 추적할 방법이 없었다고 하며 메모리 해제(unset하여 refcount 0 만들기)
해도 PHP는 여전히 참조하고 있어 메모리 누수가 이어진다. 아래 소스를 보자.
/* create the two entities, objects here */
$a = new ObjA;
$b = new ObjB;
/* create the circular reference between them */
$a->b = $b;
$b->a = $a;
/* Decrement refcount of both entities */
unset($a, $b);
이 상황에서 PHP는 두 `zvals`를 패치할 방법이 없다, 서로 소유하고 있기때문에 메모리에 여전히 남아있고, PHP 5.3 이전에는 절대 메모리를 해제할 수 없다
PHP는 때때로
자동으로 순환참조를 찾아 해제한다. 당장 메모리 해제를 하고 싶다면 gc_collect_cycles();
를 사용하면 된다.
아래는 예제이다.
/* create the two entities */
$a = new ObjA;
$b = new ObjB;
/* create the circular reference between them */
$a->b = $b;
$b->a = $a;
/* Decrement refcount of both entities */
unset($a, $b);
/* Force PHP to clean the circular reference */
$cleaned = gc_collect_cycles();
/* Display how many zvals have been freed by
the circular reference garbage collector */
echo $cleaned; /* 2 */
순환참조는 메모리를 먹는다. 이는 웹 환경에서는 그리 심각하지 않을 수 있다. 요청이 끝나면 모든 자원을 자동으로 비우기 때문인데 순환참조도 이에 당연히 포함된다.
문제는 지속적인 스크립트나 데몬에서 발생할 수 있다. 이럴 때 Zend GC를 사용할 수 있다.
PHP가 실제로 어떻게 메모리를 가지고 노는지 알아보았다.
zval
이라는 용기를 사용하여 변수를 나타내고, 각 zval
을 가리키는 수를 추적하여 그 수가 0에 도달 할 때 zval
메모리를 해제한다.
PHP는 변수를 복사할 때 효율적이지만 참조변수를 많이 사용하면 추적하기 쉽지 않고, 의도치 않게 zval
을 복사 할 수 있다. 일반적으로 구현하려는 것에 97%는 참조를 사용하지 않고 수행될 수 있으므로 일반적으로는 참조를 사용하지 않아야 한다.
하지만 모든 참조변수가 메모리를 저장하는 것은 아니다. 이제는 알고 사용하자.