PHP의 변수 관리법2

PHP는 어떨때 메모리 누수가 발생할까?

Posted by PAPION on November 12, 2019 · 3 mins read #PHP

PHP의 변수 관리2

전 포스팅에 이어 이제 실제 Memory leak, gc 에 관해 알아보자

Memory leaks and garbage collection

먼저 garbage collection이 무엇인지 정의하고 시작하자. Garbage collection은 object의 메모리 사용을 추적하고, 또는 현재 범위에서 도달하지 못하는..그리고 프로그래머에 의해 사용되고 있는 메모리를 추적하는 메커니즘이다. 지난 포스팅에서 봤듯이 메모리해제는 zvalsrefcount가 0이될 때 해제되는 동일한 메커니즘이다.

PHP는 5.3버전 부터 등장하기 시작했다. Zend Grabage Collector or ZendGC 는 메모리를 자동으로 해제할 뿐더러 순환 참조(circular references)도 처리한다. Garbage Collection 참조

순환 참조는 objectsarray와 같은 혼합 타입에서 나타난다. 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%는 참조를 사용하지 않고 수행될 수 있으므로 일반적으로는 참조를 사용하지 않아야 한다. 하지만 모든 참조변수가 메모리를 저장하는 것은 아니다. 이제는 알고 사용하자.

Reference