PHP로 메모리를 비우는 데 더 좋은 점 : unset () 또는 $ var = null


244

두 번째는 함수 호출의 오버 헤드를 피한다는 것을 알고 있습니다 ( update , 실제로 언어 구성입니다). 하나가 다른 것보다 낫다면 흥미로울 것입니다. 나는 unset()대부분의 코딩에 사용하고 있지만 최근에는 $var = null대신에 그물에서 발견되는 존경할만한 몇 가지 클래스를 살펴 보았습니다 .

선호하는 것이 있습니까? 추론은 무엇입니까?

답변:


234

2009 년 설정되지 않은 매뉴얼 페이지에서 언급되었습니다 .

unset()변수 이름을 설정합니다. 메모리를 즉시 해제하지 않습니다. PHP의 가비지 콜렉터는 CPU 사이클이 필요하지 않거나 스크립트가 메모리 부족으로 늦어지기 전에 늦게까지 의도적으로 적합 할 때이를 수행합니다.

당신이하고 있다면 $whatever = null;변수의 데이터를 다시 쓰고 있습니다. 메모리를 더 빨리 확보 / 축소 할 수 있지만 실제로 필요한 코드에서 CPU주기를 훔쳐서 전체 실행 시간이 길어질 수 있습니다.

(2013 년 이후로 해당 unset매뉴얼 페이지 에는 해당 섹션이 더 이상 포함되지 않습니다)

php5.3까지 부모-자식 관계와 같이 순환 참조에 두 개의 객체가있는 경우 부모 객체에서 unset ()을 호출하면 자식 객체의 부모 참조에 사용 된 메모리가 해제되지 않습니다. (부모 객체가 가비지 수집 될 때 메모리가 해제되지 않습니다.) ( 버그 33595 )


" unset과 = null의 차이점 "이라는 질문에는 몇 가지 차이점이 자세히 설명되어 있습니다.


unset($a)$a기호 테이블에서 제거 합니다. 예를 들면 다음과 같습니다.

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

출력 :

Notice: Undefined variable: a in xxx
NULL

그러나 언제 $a = null사용됩니까?

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:

NULL

그 보인다 $a = null조금 더 빨리 그보다 unset()기호 테이블 항목을 업데이트하면 빨리 제거보다 나타납니다 : 대응.


  • 존재하지 않는 ( unset) 변수 를 사용하려고 하면 오류가 발생하고 변수 표현식의 값이 널이됩니다. (PHP는 다른 무엇을해야합니까? 모든 표현식은 어느 정도 가치가 있어야합니다.)
  • null이 할당 된 변수는 여전히 완벽하게 정상적인 변수입니다.

18
경우 참고 $whatever객체에 대한 포인트, $whatever = null포인터를 덮어 객체 자체가 아니라, 그래서 그것은 기본적와 같은 역할을합니다 unset().
Gras Double

1
@VonC : 당신이 말하는 php.net에 설정되지 않은 인용문이 더 이상 존재하지 않습니다.
Jürgen Thelen

@ JürgenThelen 사실이지만 이전 답변의 내용은 여전히 ​​관련성이있는 것 같습니다.
VonC

1
@VonC : 물론입니다. "CPU주기가 필요하지 않음"에 대해 잘 모르겠으며 "메모리 부족 전"메모리 가비지 수집을 트리거합니다. stackoverflow.com/q/20230626/693207을 참조하십시오 . 어쩌면 당신은 약간의 빛을 비출 수 있습니까?
Jürgen Thelen 12

1
@Omar 대답을 편집했습니다. 2009의 설정되지 않은 매뉴얼 페이지 (2009 버전에 연결됨)에 더 이상 동일한 페이지의 현재 버전에없는 섹션이 포함되어 있습니다.
VonC

48

unset실제로 함수는 아니지만 언어 구문 입니다. 더 이상 a return또는 a보다 함수 호출이 아닙니다 include.

성능 문제 외에도 unset코드를 사용 하면 코드의 의도가 훨씬 명확 해집니다.


그래서 나는 항상 그것들을 사용했습니다. 개인적으로 나는 그들이 $ var = null보다 더 좋아 보인다고 생각했습니다. 그건 그렇고, 항상 NULL 전체 대문자를 사용했지만 ... 지금은 왜 그런지 모르겠습니다.
alex

1
@ VonC : 예, 알았습니다. 그러나 왜 소문자 true, false 및 null을 사용할 수 있습니까?
alex

3
@alex, 당신은 일종의 설정되지 않은 것으로 할 수 있습니다. 예를 들어 "$ test = 4; (unset) $ test;" -이상하지만 사실이며 설정을 해제하기 전에 $ test 값을 반환합니다. 어쨌든, PHP 매뉴얼은 언어 구조인지 확인합니다.
thomasrutter

5
@alex : PSR-2 모든 키워드에 소문자가 필요합니다 .
Tgr

2
@alex-PHP 키워드는 대소 문자를 구분하지 않습니다. 당신은 또한 철자 수 unset와 같은 UnSeT예를 들어,. 커뮤니티는 스타일 문제로 모두 소문자로 설정했지만 다른 케이스는 여전히 작동합니다.
Mark Reed

35

변수에 대해 unset ()을 수행하면 본질적으로 변수를 '가비지 수집'(PHP에는 실제로는 없지만 예를 들어)으로 표시하므로 메모리를 즉시 사용할 수 없습니다. 변수는 더 이상 데이터를 저장하지 않지만 스택은 더 큰 크기로 유지됩니다. null 메서드를 수행하면 데이터가 삭제되고 스택 메모리가 거의 즉시 축소됩니다.

이것은 개인적인 경험과 다른 사람들로부터 온 것입니다. unset () 함수의 주석은 here을 참조하십시오 .

개인적으로 루프에서 반복 사이에 unset ()을 사용하므로 스택 크기가 지연 될 필요가 없습니다. 데이터는 사라졌지 만 설치 공간은 남아 있습니다. 다음 반복에서 메모리는 이미 PHP에 의해 취해지고 있으므로 다음 변수를 더 빨리 초기화합니다.


15
NULL 값을 보유하는 데 필요한 메모리가 이전에 보유한 값을 보유하는 데 필요한 메모리보다 작은 경우 무언가를 NULL로 설정하면 도움이 될 수 있습니다. 예를 들어 긴 문자열입니다. 문자열이 상수가 아니고 참조 카운트가 0으로 떨어지면 해당 메모리를 해제해야합니다. 설정 해제는 더 깨끗합니다. 더 이상 참조를 유지하지 않습니다. 가비지 콜렉션을 기다려야하지만 메모리가 부족하면 가비지 콜렉션이 트리거되므로 메모리가없는 것으로 간주하는 것이 안전합니다.
thomasrutter

둘 다 사용할 수 없습니까? null과 같고 설정이 해제 되었습니까?
Nabeel Khan

2
@NabeelKhan 루프 내에서 unset ()을 사용하고 루프를 종료하면 무효화하는 것이 좋습니다. 그렇지 않으면 루프 내부에서 두 가지를 모두 수행 할 때 성능에 영향을 미칩니다. 루프를 사용하지 않는 경우 장면 뒤의 unset () 논리를 이미 수행하므로 무효화됩니다.
William Holroyd

27
<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";
?>

"= null"이 더 빠를 것 같습니다.

PHP 5.4 결과 :

  • 0.88389301300049 초 소요
  • 2.1757180690765 초가 걸렸습니다.

PHP 5.3 결과 :

  • 1.7235369682312 초가 걸렸습니다.
  • 2.9490959644318 초 걸렸다

PHP 5.2 결과 :

  • 3.0069220066071 초 걸렸다
  • 4.7002630233765 초가 걸렸습니다.

PHP 5.1 결과 :

  • 2.6272349357605 초 걸렸다
  • 5.0403649806976 초가 걸렸다

PHP 5.0과 4.4에서는 상황이 다르게 보이기 시작합니다.

5.0 :

  • 10.038941144943 초 걸렸다
  • 7.0874409675598 초 소요

4.4 :

  • 소요 7.5352551937103 초
  • 6.6245851516724 초가 걸렸습니다.

microtime (true)은 PHP 4.4에서 작동하지 않으므로 php.net/microtime / Example # 1에 제공된 microtime_float 예제를 사용해야했습니다.


7
테스트에 결함이 있다고 생각합니다. 첫 번째 루프는 간단한 재 할당이고 두 번째 루프는 동일한 심볼을 파괴하고 다시 만듭니다. 배열로 테스트를 다시 수행하면 unset속도가 더 빠릅니다. 나중에 unset사건의 존재를 확인하는 테스트가 있습니다. 이 테스트에서는 null약간 더 빠릅니다. 테스트 : pastebin.com/fUe57C51
Knyri

4
@ansur, gc_collect_cycles더 정확한 결과를 얻으려면 항상 타이머를 시작하기 전에 전화 하십시오.
Pacerier

@knyri 당신은 그것에 링크 할 수 있습니까?
Nabeel Khan

@NabeelKhan 더 이상 해당 테스트 결과가 없습니다. 그러나 이전 주석에는 테스트 코드에 대한 링크가 있습니다.
Knyri

19

배열 요소와 차이를 만듭니다.

이 예를 고려하십시오

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

여기에는 키 '테스트'가 여전히 존재합니다. 그러나이 예에서는

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

키가 더 이상 존재하지 않습니다.


18

참조로 복사 된 변수에 대해 다른 방식으로 작동합니다.

$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5

$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null

5
나는 몇 년 전에 PHP를 코딩 해 왔으며 원래 var를 참조하는 것에 대해 "&"를 보지 못했습니다. 감사합니다 + 1 :)
Chris

1
$ a = 78; $ b = $ a; 미 설정 ($ a); var_dump ($ b); // 78; var_dump ($ a); // 정의되지 않은 변수 : a
zloctb

13

객체, 특히 지연로드 시나리오에서 가비지 콜렉터가 유휴 CPU 주기로 실행 중임을 고려해야하므로 많은 오브젝트가 작은 시간 페널티를로드 할 때 문제가 발생한다고 가정하면 메모리 확보가 해결됩니다.

GC가 메모리를 수집 할 수있게하려면 time_nanosleep을 사용하십시오. 변수를 null로 설정하는 것이 바람직합니다.

프로덕션 서버에서 테스트 한 결과 원래 작업이 50MB를 소비 한 후 중지되었습니다. nanosleep을 사용한 후 14MB는 일정한 메모리 소비였습니다.

PHP 버전에서 버전으로 변경 될 수있는 GC 동작에 따라 달라집니다. 그러나 그것은 PHP 5.3에서 잘 작동합니다.

예. 이 샘플 (VirtueMart2 Google 피드에서 가져온 코드)

for($n=0; $n<count($ids); $n++)
{
    //unset($product); //usefull for arrays
    $product = null
    if( $n % 50 == 0 )
    {
        // let GC do the memory job
        //echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
        time_nanosleep(0, 10000000);
    }

    $product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
    ...

3

나는 여전히 이것에 대해 의심하지만 스크립트에서 시도했지만 xdebug를 사용하여 앱 메모리 사용에 미치는 영향을 알고 있습니다. 스크립트는 다음과 같이 내 기능에 설정됩니다.

function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
    $sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
    if($showSql === FALSE) {
        $sql = mysql_query($sql) or die(mysql_error());
        $data = mysql_fetch_array($sql);
        return $data[0];
    } else echo $sql;
}

그리고 return코드 바로 앞에 unset을 추가 하고 나에게 160200을 주면 다음과 같이 변경하려고합니다.$sql = NULL 주고 그것을 바꾸려고하면 나에게 : 160224 :)

그러나 unset () 또는 NULL을 사용하지 않을 때이 비교에서 고유 한 것이 있습니다 .xdebug는 메모리 사용량으로 160144를 제공합니다.

따라서 unset () 또는 NULL을 사용하도록 줄을 지정하면 응용 프로그램에 프로세스가 추가되고 코드에서 원점을 유지하고 가능한 한 효과적으로 사용하는 변수를 줄이는 것이 좋습니다.

내가 틀렸다면 고쳐줘, 고마워


$ data [0] 항목을 반환하는 동안 전체 배열이 참조되지만 가설 일뿐입니다. $ data [0]을 로컬 변수에 복사하고 배열을 null로 설정 한 다음 로컬 변수를 반환하십시오. 좋은 배경은 여기 tuxradar.com/practicalphp/18/1/11ofc 입니다. php.net/manual/en/features.gc.php
OSP

2

주석에서 언급했듯이 여기에 작성된 오류 (요소 재생성)가 있기 때문에 unsetand =null에 대한 새로운 성능 테스트를 만들었습니다 . 지금은 중요하지 않은 것처럼 배열을 사용했습니다.

<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = 'a';
    $arr2[$i] = 'a';
}

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = null;
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

그러나 PHP 5.5.9 서버에서만 테스트 할 수 있습니다. 결과는 다음과 같습니다.-4.4571571350098 초 소요-4.4425978660583 초 소요

unset가독성을 위해 선호 합니다.


2

PHP 7은 이미 그러한 메모리 관리 문제와 최소한의 사용으로 줄어 들었습니다.

<?php
  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

  $start = microtime(true);
  for ($i = 0; $i < 10000000; $i++) {
     $a = 'a';
     unset($a);
  }
  $elapsed = microtime(true) - $start;

  echo "took $elapsed seconds\r\n";

?>

PHP 7.1 Outpu :

0.16778993606567 초 소요 0.16630101203918 초 소요


1

unset즉각적인 메모리를 확보하지 않으면 코드는 여전히 매우 유용하며 메소드를 종료하기 전에 코드 단계를 전달할 때마다이를 수행하는 것이 좋습니다. 즉시 메모리를 비우는 것이 아니라는 점에 유의하십시오. 즉각적인 메모리는 CPU를위한 것이며, 보조 메모리는 RAM입니다.

또한 메모리 누수 방지에 대해서도 다룹니다.

이 링크를 참조하십시오 http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

나는 오랫동안 오랫동안 설정 해제를 사용하고 있습니다.

이미 배열로 사용 된 모든 변수를 설정 해제하기 위해 코드에서 이와 같은 더 나은 방법.

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

그리고 just unset($data);모든 변수의 사용을 해제합니다.

설정을 해제하려면 관련 항목을 참조하십시오

PHP에서 변수를 설정 해제하는 것이 얼마나 중요합니까?

[곤충]


1

기록을 위해, 그리고 걸리는 시간을 제외하고 :

<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n";

echo "<hr>function:<br>";
function test() {
    $x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

반환

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

결론 : 예상대로 널 (null) 및 설정되지 않은 사용 가능한 메모리 (실행이 끝날 때만). 또한 변수를 재 할당하면 특정 시점에서 값이 두 번 유지됩니다 (520216 대 438352).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.