Apa yang lebih baik dalam membebaskan memori dengan PHP: unset () atau $ var = null

244

Saya menyadari yang kedua menghindari overhead panggilan fungsi ( pembaruan , sebenarnya adalah konstruksi bahasa), tetapi akan menarik untuk mengetahui apakah yang satu lebih baik dari yang lain. Saya telah menggunakan unset()sebagian besar pengkodean saya, tetapi saya baru-baru ini melihat beberapa kelas terhormat yang ditemukan di internet yang menggunakan $var = null.

Apakah ada yang disukai, dan apa alasannya?

alex
sumber

Jawaban:

234

Itu disebutkan di halaman manual yang tidak disetel pada 2009 :

unset()tidak hanya apa yang namanya - hapus variabel. Itu tidak memaksa pembebasan memori segera. Pemulung sampah PHP akan melakukannya ketika cocok - dengan niat segera, karena siklus CPU itu tidak diperlukan, atau selambat-lambatnya sebelum skrip kehabisan memori, apa pun yang terjadi terlebih dahulu.

Jika Anda melakukannya $whatever = null;maka Anda menulis ulang data variabel. Anda mungkin mendapatkan memori lebih cepat / menyusut lebih cepat, tetapi mungkin mencuri siklus CPU dari kode yang benar-benar membutuhkannya lebih cepat, menghasilkan waktu eksekusi keseluruhan yang lebih lama.

(Sejak 2013, unsethalaman manual itu tidak termasuk bagian itu lagi)

Perhatikan bahwa hingga php5.3, jika Anda memiliki dua objek dalam referensi melingkar , seperti dalam hubungan orangtua-anak, panggilan tidak disetel () pada objek induk tidak akan membebaskan memori yang digunakan untuk referensi induk dalam objek anak. (Memori juga tidak akan dibebaskan ketika objek induk dikumpulkan dari sampah.) ( Bug 33595 )


Pertanyaan " perbedaan antara tidak disetel dan = nol " merinci beberapa perbedaan:


unset($a)juga menghapus $adari tabel simbol; sebagai contoh:

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

Keluaran:

Notice: Undefined variable: a in xxx
NULL

Tapi kapan $a = nulldigunakan:

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

NULL

Tampaknya itu $a = nullsedikit lebih cepat daripada unset()rekannya: memperbarui entri tabel simbol tampaknya lebih cepat daripada menghapusnya.


  • ketika Anda mencoba menggunakan unsetvariabel yang tidak ada ( ), kesalahan akan dipicu dan nilai untuk ekspresi variabel akan nol. (Karena, apa lagi yang harus dilakukan PHP? Setiap ekspresi perlu menghasilkan nilai tertentu.)
  • Variabel dengan null ditugaskan untuk itu masih merupakan variabel normal.
VONC
sumber
18
Perhatikan bahwa jika $whatevermenunjuk ke suatu objek, $whatever = nulltimpa pointer, bukan objek itu sendiri, sehingga pada dasarnya sama dengan unset().
Gras Double
1
@VonC: kutipan yang tidak disetel pada php.net yang Anda maksud tidak ada lagi.
Jürgen Thelen
@ JürgenThelen benar, tetapi isi dari jawaban lama itu masih relevan, bukan?
VonC
1
@VonC: Pasti. Saya hanya tidak yakin tentang "siklus CPU tidak diperlukan" dan "sebelum .. kehabisan memori" memicu pengumpulan sampah. Lihat stackoverflow.com/q/20230626/693207 . Mungkin Anda bisa menjelaskan?
Jürgen Thelen
1
@Omar Saya telah mengedit jawaban: Halaman manual yang belum disetel dari 2009 (saya telah ditautkan ke versi 2009) tidak menyertakan bagian yang tidak lagi ada dalam versi saat ini dari halaman yang sama.
VonC
48

unsetsebenarnya bukan fungsi, tetapi konstruksi bahasa . Ini tidak lebih dari panggilan fungsi daripada a returnatau a include.

Selain masalah kinerja, penggunaan unsetmembuat maksud kode Anda lebih jelas.

Alex Barrett
sumber
Itu sebabnya saya selalu menggunakannya, secara pribadi saya pikir mereka terlihat lebih baik daripada $ var = null. Omong-omong, saya selalu menggunakan topi penuh NULL ... tapi sekarang saya tidak tahu mengapa?
alex
1
@VonC: Ya, saya sudah tahu itu, tapi mengapa Anda bisa menggunakan huruf kecil benar, salah dan nol?
alex
3
@alex, Anda bisa melakukan itu dengan tidak disetel. Misalnya "$ test = 4; (tidak disetel) $ test;" - aneh tapi benar, dan mengembalikan nilai $ test sebelum membukanya. Apapun, manual PHP tidak mengkonfirmasi bahwa itu adalah konstruksi bahasa.
thomasrutter
5
@alex: PSR-2 membutuhkan huruf kecil untuk semua kata kunci.
Tgr
2
@alex - Kata kunci PHP tidak peka huruf besar-kecil; Anda juga bisa mengeja unsetsebagai UnSeT, misalnya. Komunitas telah memilih huruf kecil semua sebagai masalah gaya, tetapi selubung lainnya masih berfungsi.
Mark Reed
35

Dengan melakukan unset () pada variabel, Anda pada dasarnya telah menandai variabel untuk 'pengumpulan sampah' (PHP tidak benar-benar memilikinya, tetapi sebagai contohnya) sehingga memori tidak segera tersedia. Variabel tidak lagi menampung data, tetapi tumpukan tetap pada ukuran yang lebih besar. Melakukan metode nol menjatuhkan data dan mengecilkan memori tumpukan segera.

Ini berasal dari pengalaman pribadi dan juga yang lainnya. Lihat komentar fungsi unset () di sini .

Saya pribadi menggunakan unset () di antara iterasi dalam satu lingkaran sehingga saya tidak harus memiliki keterlambatan tumpukan yang ukurannya yo-yo'd. Data hilang, tetapi jejak tetap ada. Pada iterasi berikutnya, memori sudah diambil oleh php dan karenanya, lebih cepat menginisialisasi variabel berikutnya.

William Holroyd
sumber
15
Menyetel sesuatu ke NULL dapat bermanfaat jika memori yang dibutuhkan untuk menyimpan nilai NULL kurang dari yang diperlukan untuk menyimpan nilai apa pun yang sebelumnya dipegang. Misalnya, string panjang. Jika string bukan konstanta dan jumlah referensi turun menjadi nol, maka memori itu harus dibebaskan. Unset lebih bersih - tidak lagi mempertahankan referensi. Anda memang harus menunggu pengumpulan sampah, tetapi aman untuk memperlakukannya sebagai tidak menempati memori, karena kondisi memori yang rendah akan memicu pengumpulan sampah.
thomasrutter
tidak bisakah kita menggunakan keduanya? sama dengan nol lalu tidak disetel?
Nabeel Khan
2
@NabeelKhan Saya sarankan menggunakan unset () di dalam loop dan kemudian membatalkannya ketika Anda keluar dari loop. Kalau tidak, ada dampak kinerja untuk melakukan keduanya di dalam loop. Jika Anda tidak menggunakan loop, maka batalkan saja karena sudah membuat logika unset () di belakang layar.
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";
?>

Per itu sepertinya "= null" lebih cepat.

Hasil PHP 5.4:

  • butuh 0,88389301300049 detik
  • butuh 2,1757180690765 detik

Hasil PHP 5.3:

  • butuh 1,7235369682312 detik
  • butuh 2,9490959644318 detik

Hasil PHP 5.2:

  • butuh 3,0069220066071 detik
  • butuh 4,7002630233765 detik

Hasil PHP 5.1:

  • butuh 2,6272349357605 detik
  • mengambil 5,0403649806976 detik

Hal-hal mulai terlihat berbeda dengan PHP 5.0 dan 4.4.

5.0:

  • butuh 10,038941144943 detik
  • butuh 7,0874409675598 detik

4.4:

  • butuh 7,5352551937103 detik
  • butuh 6,6245851516724 detik

Perlu diingat microtime (true) tidak berfungsi dalam PHP 4.4 jadi saya harus menggunakan contoh microtime_float yang diberikan di php.net/microtime / Contoh # 1.


sumber
7
Saya pikir tes Anda cacat. Loop pertama adalah penugasan ulang sederhana dan loop kedua menghancurkan dan menciptakan kembali simbol yang sama. Jika tes redone dengan array unsetlebih cepat. Saya memiliki tes yang kemudian memeriksa keberadaannya dalam unsetkasus ini. Dalam tes itu pengaturannya menjadi nullsedikit lebih cepat. Uji: pastebin.com/fUe57C51
Knyri
4
@ansur, selalu telepon gc_collect_cyclessebelum memulai timer untuk mendapatkan hasil yang lebih akurat.
Pacerier
@knyri dapatkah Anda menautkan ke sana?
Nabeel Khan
@NabeelKhan Saya tidak lagi memiliki hasil tes itu; tetapi ada tautan ke kode tes di komentar saya sebelumnya.
Knyri
19

Itu membuat perbedaan dengan elemen array.

Pertimbangkan contoh ini

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

Di sini, 'tes' kuncinya masih ada. Namun, dalam contoh ini

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

kunci tidak ada lagi.

auris
sumber
18

Ia bekerja dengan cara berbeda untuk variabel yang disalin dengan referensi:

$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
RiaD
sumber
5
Saya telah mengkode php beberapa tahun sekarang dan tidak pernah melihat "&" tentang referensi var asli. Terima kasih +1 :)
Chris
1
$ a = 78; $ b = $ a; tidak disetel ($ a); var_dump ($ b); // 78; var_dump ($ a); // Variabel tidak
terdefinisi
13

Mengenai objek, terutama dalam skenario lazy-load, orang harus mempertimbangkan pengumpul sampah berjalan dalam siklus CPU menganggur, jadi anggap Anda akan mendapat masalah ketika banyak objek memuat hukuman kecil waktu akan menyelesaikan pembebasan memori.

Gunakan time_nanosleep untuk mengaktifkan GC untuk mengumpulkan memori. Pengaturan variabel ke nol diinginkan.

Diuji pada server produksi, awalnya pekerjaan tersebut menghabiskan 50MB dan kemudian dihentikan. Setelah nanosleep digunakan 14MB adalah konsumsi memori yang konstan.

Orang harus mengatakan ini tergantung pada perilaku GC yang dapat berubah dari versi PHP ke versi. Tetapi berfungsi dengan baik di PHP 5.3.

misalnya. contoh ini (kode diambil dari VirtueMart2 google feed)

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);
    ...
OSP
sumber
3

Saya masih ragu tentang ini, tetapi saya sudah mencobanya di skrip saya dan saya menggunakan xdebug untuk mengetahui bagaimana itu akan mempengaruhi penggunaan memori aplikasi saya. Script diatur pada fungsi saya seperti ini:

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

Dan saya menambahkan tidak diset sebelum returnkode dan itu memberi saya: 160200 maka saya mencoba mengubahnya dengan $sql = NULLdan itu memberi saya: 160224 :)

Tetapi ada sesuatu yang unik pada perbandingan ini ketika saya tidak menggunakan unset () atau NULL, xdebug memberi saya 160144 sebagai penggunaan memori

Jadi, saya pikir memberikan baris untuk menggunakan unset () atau NULL akan menambahkan proses ke aplikasi Anda dan akan lebih baik untuk tetap asal dengan kode Anda dan mengurangi variabel yang Anda gunakan seefektif mungkin.

Koreksi saya jika saya salah, terima kasih

Anggie Aziz
sumber
Saya pikir saat Anda mengembalikan $ data [0] item, seluruh array direferensikan / tapi itu hanya hipotesis. Cobalah untuk menyalin $ data [0] ke variabel lokal, atur array ke nol dan kembalikan variabel lokal. Latar belakang yang baik ada di sini tuxradar.com/practicalphp/18/1/11 dan ofc. php.net/manual/en/features.gc.php
OSP
2

Saya membuat tes kinerja baru untuk unsetdan =null, karena seperti yang disebutkan dalam komentar yang ditulis di sini memiliki kesalahan ( penciptaan ulang elemen). Saya menggunakan array, seperti yang Anda lihat tidak masalah sekarang.

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

Tetapi saya hanya dapat mengujinya di server PHP 5.5.9, di sini hasilnya: - butuh 4,4571571350098 detik - butuh 4,4425978660583 detik

Saya lebih suka unsetuntuk alasan keterbacaan.

Michael B.
sumber
2

PHP 7 sudah bekerja pada masalah manajemen memori seperti itu dan penggunaannya yang dikurangi hingga minimal.

<?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:

mengambil 0,16778993606567 detik mengambil 0,16630101203918 detik

Swapnil
sumber
1

unsetkode jika tidak membebaskan memori langsung masih sangat membantu dan akan menjadi praktik yang baik untuk melakukan ini setiap kali kita meneruskan langkah-langkah kode sebelum kita keluar dari suatu metode. perhatikan ini bukan tentang membebaskan memori langsung. memori langsung untuk CPU, bagaimana dengan memori sekunder yaitu RAM.

dan ini juga menangani tentang mencegah kebocoran memori.

silakan lihat tautan ini http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

Saya telah menggunakan unset untuk waktu yang lama sekarang.

praktik yang lebih baik seperti ini dalam kode untuk secara instan menghapus semua variabel yang telah digunakan sebagai array.

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

dan just unset($data);untuk membebaskan semua penggunaan variabel.

silakan lihat topik terkait untuk tidak disetel

Seberapa pentingkah menghapus variabel di PHP?

[bug]

nol8
sumber
1

Sebagai catatan, dan tidak termasuk waktu yang diperlukan:

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

Ia kembali

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

Kesimpulannya, memori bebas nol dan tidak disetel seperti yang diharapkan (tidak hanya pada akhir eksekusi). Juga, menetapkan ulang suatu variabel memegang nilai dua kali pada beberapa titik (520216 versus 438352)

magallanes
sumber