PHP ile bellek boşaltmada daha iyi ne var: unset () veya $ var = null


244

İkincisinin bir işlev çağrısının ek yükünü önlediğini ( güncelleme , aslında bir dil yapısıdır), ancak birinin diğerinden daha iyi olup olmadığını bilmek ilginç olacaktır. Kodlamamın çoğu için kullanıyorum unset(), ancak son zamanlarda $var = nullbunun yerine kullanılan netin dışında bulunan birkaç saygın sınıftan baktım .

Tercih edilen bir tane var mı ve mantığı nedir?

Yanıtlar:


234

2009 yılında unset kılavuzunun sayfasında belirtilmiştir :

unset()sadece adının söylediği şeyi yapar - bir değişkenin ayarını kaldırın. Anında bellek boşaltmaya zorlamaz. PHP'nin çöp toplayıcısı, bu CPU döngülerine zaten ihtiyaç duyulmadığı için veya komut dosyasında bellek bitmeden önce olduğu gibi, nihayetinde, uygun gördüğünde bunu yapacak.

Bunu yapıyorsanız $whatever = null;, değişkenin verilerini yeniden yazıyorsunuz demektir. Belleği daha hızlı serbest bırakabilir / daraltabilirsiniz, ancak CPU döngülerini gerçekten onlara daha erken ihtiyaç duyan koddan çalabilir ve bu da daha uzun bir genel yürütme süresine neden olabilir.

(2013'ten bu yana, bu unsetsayfa artık bu bölümü içermiyor)

Php5.3'e kadar, bir üst-alt ilişkisi gibi dairesel başvuruda iki nesneniz varsa , üst nesnede unset () çağrıldığında alt nesnede üst başvuru için kullanılan bellekte boş alan olmayacağını unutmayın. (Üst nesne çöp toplandığında bellek de serbest bırakılmaz.) ( Hata 33595 )


" Unset ve = null arasındaki fark " sorusu bazı farklılıkları açıklar :


unset($a)ayrıca $asembol tablosundan kaldırır ; Örneğin:

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

Çıktılar:

Notice: Undefined variable: a in xxx
NULL

Ancak ne zaman $a = nullkullanılır:

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

NULL

Görünüşe göre karşıtından $a = nullbiraz daha hızlı unset(): Bir sembol tablosu girişini güncellemek, kaldırmaktan daha hızlı görünüyor.


  • varolmayan bir ( unset) değişkeni kullanmaya çalıştığınızda bir hata tetiklenir ve değişken ifadesinin değeri null olur. (Çünkü PHP başka ne yapmalı? Her ifadenin bir değer oluşturması gerekir.)
  • Buna null atanmış bir değişken yine de tamamen normal bir değişkendir.

18
$whateverBir nesneye işaret ederse $whatever = null, nesnenin kendisinin değil, işaretçinin üzerine yazıldığından, temel olarak aynı şekilde hareket ettiğini unutmayın unset().
Gras Double

1
@VonC: bahsettiğiniz php.net'teki ayarlanmamış teklif artık mevcut değil.
Jürgen Thelen

@ JürgenThelen doğru, ama bu eski cevabın içeriği hala alakalı görünüyor, değil mi?
VonC

1
@VonC: Kesinlikle. "CPU döngüleri gerekli değil" ve "daha önce .. bellek yetersiz" hakkında emin değilim çöp toplama tetikler. Bkz. Stackoverflow.com/q/20230626/693207 . Belki biraz ışık tutabilirsin?
Jürgen Thelen

1
@Omar Cevabı düzenledim: 2009'dan (2009 sürümüne bağlandım) ayarlanmamış el sayfası, aynı sayfanın geçerli sürümünde artık bulunmayan bir bölüm içeriyor.
VonC

48

unsetaslında bir işlev değil, bir dil yapısıdır . Bir returnveya bir değil, bir işlev çağrısı değildir include.

Performans sorunlarının yanı sıra, kullanmak unsetkodunuzun amacını daha açık hale getirir .


Bu yüzden onları her zaman kullandım, kişisel olarak $ var = null'dan daha iyi göründüklerini düşündüm. Bu arada, her zaman NULL dolu kapaklar kullandım ... ama şimdi nedenini bilmiyorum?
alex

1
@VonC: Evet, bunu anladım, ama neden küçük harfli doğru, yanlış ve boş kullanabilirsiniz?
alex

3
@alex, bunu unset ile yapabilirsiniz. Örneğin "$ test = 4; (unset) $ test;" - garip ama doğru, ve ayarlamadan önce $ test değerini döndürür. Ne olursa olsun, PHP el kitabı bunun bir dil yapısı olduğunu onaylar.
thomasrutter

5
@alex: PSR-2 , tüm anahtar kelimeler için küçük harf gerektirir .
Tgr

2
@alex - PHP anahtar sözcükleri büyük / küçük harfe duyarsızdır; Ayrıca yazım olabilir unsetolarak UnSeTörneğin. Topluluk bir stil meselesi olarak küçük harfe yerleşti, ancak diğer muhafazalar hala çalışıyor.
Mark Reed

35

Değişken üzerinde bir unset () yaparak, aslında 'çöp toplama' değişkenini işaretlediniz (PHP gerçekten bir tane yok, ama örneğin aşkına), böylece bellek hemen kullanılamaz. Değişken artık verileri barındırmaz, ancak yığın daha büyük boyutta kalır. Null yönteminin yapılması verileri düşürür ve yığın belleğini neredeyse anında küçültür.

Bu kişisel deneyimlerden ve diğerlerinden de kaynaklanıyordu. Burada unset () işlevinin açıklamalarına bakın .

Şahsen bir döngüde yinelemeler arasında unset () kullanıyorum, böylece yığının yo-yo'd olmasının gecikmesine gerek yok. Veriler kayboldu, ancak kapladığı alan kaldı. Bir sonraki yinelemede, bellek zaten php tarafından alınır ve böylece bir sonraki değişkeni başlatmak daha hızlıdır.


15
Bir şeyi NULL olarak ayarlamak, NULL değerini tutmak için gereken bellek daha önce tuttuğu değeri tutmak için gerekli olandan daha azsa yararlı olabilir. Örneğin, uzun bir dize. Dize sabit değilse ve referans sayısı sıfıra düşerse, o bellek serbest bırakılmalıdır. Ayarı daha temiz - artık referans sağlamaz. Çöp toplanmasını beklemek zorundasınız, ancak hafızayı işgal etmediğini düşünmek güvenlidir, çünkü düşük bellek durumu çöp toplanmasını tetikleyecektir.
thomasrutter

ikisini de kullanamaz mıyız null değerine eşit ve sonra unset?
Nabeel Khan

2
@NabeelKhan Döngüler içinde unset () kullanmanızı ve döngüden çıktığınızda bunu geçersiz kılmayı öneririm. Aksi takdirde, her ikisini de döngü içinde yapmak için bir performans etkisi vardır. Döngüler kullanmıyorsanız, sahne arkasındaki unset () mantığını zaten yaptığı gibi etkisiz hale getirin.
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";
?>

Buna göre "= null" daha hızlı görünüyor.

PHP 5.4 sonuçları:

  • 0.88389301300049 saniye sürdü
  • 2.1757180690765 saniye sürdü

PHP 5.3 sonuçları:

  • 1.7235369682312 saniye sürdü
  • 2.9490959644318 saniye sürdü

PHP 5.2 sonuçları:

  • 3.0069220066071 saniye sürdü
  • 4.7002630233765 saniye sürdü

PHP 5.1 sonuçları:

  • 2.6272349357605 saniye sürdü
  • 5.0403649806976 saniye sürdü

PHP 5.0 ve 4.4 ile işler farklı görünmeye başlar.

5.0:

  • 10.038941144943 saniye sürdü
  • 7.0874409675598 saniye sürdü

4.4:

  • 7.5352551937103 saniye sürdü
  • 6.6245851516724 saniye sürdü

Unutmayın, microtime (true) PHP 4.4'te çalışmaz, bu yüzden php.net/microtime / Örnek # 1'de verilen microtime_float örneğini kullanmak zorunda kaldım.


7
Testin hatalı olduğunu düşünüyorum. İlk döngü basit yeniden atamadır ve ikinci döngü aynı sembolü yok eder ve yeniden oluşturur. Test bir dizi ile yeniden yapılırsa unsetdaha hızlı olur. Daha sonra unsetdavada varlığını kontrol eden bir testim var. Bu testte bunu nullmarjinal olarak daha hızlı yapar. Test: pastebin.com/fUe57C51
Knyri

4
@ansur, gc_collect_cyclesdaha doğru sonuçlar almak için zamanlayıcıyı başlatmadan önce daima arayın .
Pacerier

@knyri lütfen bağlantı verebilir misiniz?
Nabeel Khan

@NabeelKhan Artık bu testin sonuçları yok; ancak önceki yorumumda test koduna bir bağlantı var.
Knyri

19

Dizi elemanları ile fark yaratır.

Bu örneği düşünün

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

Burada, 'test' anahtarı hala var. Ancak, bu örnekte

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

anahtar artık mevcut değil.


18

Referans olarak kopyalanan değişkenler için farklı bir şekilde çalışır:

$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
Ben birkaç yıl php kodlama ve orijinal var referans hakkında "&" görmedim. Teşekkürler + 1 :)
Chris

1
Bir 78 = $; $ b $, =; unset ($ a); var_dump ($ b); // 78; var_dump ($ a); // Tanımsız değişken: a
zloctb

13

Nesnelerle ilgili olarak, özellikle tembel yükleme senaryosunda, çöp toplayıcının boş CPU döngülerinde çalıştığını düşünmeliyiz, bu nedenle çok sayıda nesne küçük zaman cezası yüklediğinde sorun çıkacağınız varsayıldığında bellek boşluğu çözülecektir.

GC'nin bellek toplamasını sağlamak için time_nanosleep kullanın. Değişkenin null değerine ayarlanması istenir.

Üretim sunucusunda test edildi, başlangıçta iş 50MB tüketti ve sonra durduruldu. Nanosleep kullanıldıktan sonra 14MB sabit bellek tüketimi oldu.

Bunun PHP sürümünden sürüme değişebilen GC davranışına bağlı olduğunu söylemeliyiz. Ama PHP 5.3 üzerinde çalışıyor.

Örneğin. bu örnek (kod VirtueMart2 google feed'den alınmıştır)

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

Bu konuda hala şüphe duyuyorum, ama senaryomda denedim ve uygulama belleği kullanımını nasıl etkileyeceğini bilmek için xdebug kullanıyorum. Komut dosyası benim fonksiyonumda şu şekilde ayarlanır:

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;
}

returnKoddan hemen önce unset ekliyorum ve bana veriyor: 160200 sonra değiştirmeye çalışıyorum $sql = NULLve bana ver: 160224 :)

Ama unset () veya NULL kullanmadığımda bu karşılaştırmalı benzersiz bir şey var, xdebug bana bellek kullanımı olarak 160144 veriyor

Bu nedenle, unset () veya NULL kullanmak için satır vermek uygulamanıza işlem ekleyeceğini düşünüyorum ve kodunuzla orijin kalmak ve kullandığınız değişkeni olabildiğince etkili azaltmak daha iyi olacaktır.

Yanlışsam beni düzelt, teşekkürler


$ Data [0] öğesi döndürürken düşünüyorum, tüm dizi başvurulur / ama sadece hipotez. $ Data [0] değerini yerel değişkene kopyalamaya çalışın, diziyi null olarak ayarlayın ve yerel değişkeni döndürün. İyi bir arka plan burada tuxradar.com/practicalphp/18/1/11 ve ofc. php.net/manual/tr/features.gc.php
OSP

2

İçin yeni bir performans testi oluşturdum unsetve =nullyorumlarda belirtildiği gibi burada yazılan bir hata var (öğelerin yeniden oluşturulması). Dizileri kullandım, gördüğünüz gibi artık önemli değil.

<?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>';

Ama ben sadece bir PHP 5.5.9 sunucusunda test edebilirsiniz, burada sonuçlar: - 4.4571571350098 saniye aldı - 4.4425978660583 saniye aldı

unsetOkunabilirlik nedenlerini tercih ederim .


2

PHP 7 zaten bu tür bellek yönetimi sorunları ve azaltılmış en az kullanımı üzerinde çalışmaktadır.

<?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 Çıkışı:

0.16778993606567 saniye sürdü 0.16630101203918 saniye


1

unsetanında bellek boş değilse kod hala çok yararlı ve bir yöntem çıkmadan önce kod adımları her geçtiğimizde bunu yapmak için iyi bir uygulama olacaktır. not almak onun hemen bellek boşaltmak değil. anında bellek CPU içindir, ya ikincil bellek olan RAM.

ve bu ayrıca bellek sızıntılarının önlenmesi ile de uğraşır.

lütfen bu bağlantıya bakın http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

uzun süredir unset kullanıyorum.

daha önce dizi olarak kullanılmış olan tüm değişkenleri anında çözmek için kodda böyle daha iyi bir uygulama.

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

ve just unset($data);tüm değişken kullanımı serbest bırakmak için.

ayarını kaldırmak için lütfen ilgili konuya bakın

PHP'de değişkenleri ayarlamak ne kadar önemlidir?

[Hata]


1

Kayıt için ve gereken süre hariç:

<?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"; 

Geri döner

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

Sonuç olarak, hem boş hem de ayarlanmamış boş bellek (yalnızca yürütmenin sonunda değil). Ayrıca, bir değişkeni yeniden atamak değeri bir noktada iki kez tutar (520216 - 438352)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.