Điều gì tốt hơn trong việc giải phóng bộ nhớ với PHP: unset () hoặc $ var = null


244

Tôi nhận ra cái thứ hai tránh được chi phí của một cuộc gọi hàm ( cập nhật , thực sự là một cấu trúc ngôn ngữ), nhưng sẽ rất thú vị nếu biết cái này tốt hơn cái kia. Tôi đã sử dụng unset()cho hầu hết các mã hóa của mình, nhưng gần đây tôi đã xem qua một vài lớp đáng kính được tìm thấy ngoài mạng sử dụng $var = nullthay thế.

Có một cái ưa thích, và lý do là gì?

Câu trả lời:


234

Nó đã được đề cập trong trang hướng dẫn sử dụng unset trong năm 2009 :

unset()không đúng như tên gọi của nó - bỏ đặt một biến. Nó không buộc giải phóng bộ nhớ ngay lập tức. Trình thu gom rác của PHP sẽ làm điều đó khi nó thấy phù hợp - theo ý định ngay sau đó, vì những chu kỳ CPU đó không cần thiết, hoặc muộn nhất là trước khi tập lệnh sẽ hết bộ nhớ, bất cứ điều gì xảy ra trước tiên.

Nếu bạn đang làm $whatever = null;thì bạn đang viết lại dữ liệu của biến. Bạn có thể giải phóng bộ nhớ / thu nhỏ nhanh hơn, nhưng nó có thể đánh cắp các chu kỳ CPU từ mã thực sự cần chúng sớm hơn, dẫn đến thời gian thực hiện tổng thể lâu hơn.

(Kể từ năm 2013, unsettrang người đàn ông đó không bao gồm phần đó nữa)

Lưu ý rằng cho đến khi php5.3, nếu bạn có hai đối tượng trong tham chiếu vòng tròn , chẳng hạn như trong mối quan hệ cha-con, việc gọi unset () trên đối tượng cha sẽ không giải phóng bộ nhớ được sử dụng cho tham chiếu cha trong đối tượng con. (Bộ nhớ cũng sẽ không được giải phóng khi đối tượng cha mẹ được thu gom rác.) ( Lỗi 33595 )


Câu hỏi " sự khác biệt giữa unset và = null " nêu chi tiết một số khác biệt:


unset($a)cũng loại bỏ $akhỏi bảng biểu tượng; ví dụ:

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

Đầu ra:

Notice: Undefined variable: a in xxx
NULL

Nhưng khi $a = nullđược sử dụng:

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

NULL

Có vẻ như $a = nullnhanh hơn một chút so với unset()đối tác của nó : cập nhật mục nhập bảng biểu tượng dường như nhanh hơn loại bỏ nó.


  • khi bạn cố gắng sử dụng biến không tồn tại ( unset), một lỗi sẽ được kích hoạt và giá trị cho biểu thức biến sẽ là null. (Bởi vì, PHP nên làm gì khác? Mỗi biểu thức cần dẫn đến một số giá trị.)
  • Một biến có null được gán cho nó vẫn là một biến hoàn toàn bình thường.

18
Lưu ý rằng nếu $whatevertrỏ vào một đối tượng, $whatever = nullghi đè con trỏ, không phải chính đối tượng đó, vì vậy nó hoạt động về cơ bản giống như unset().
Gras Double

1
@VonC: trích dẫn chưa đặt trên php.net mà bạn đang đề cập đến không còn tồn tại nữa.
Jürgen Thelen

@ JürgenThelen đúng, nhưng nội dung của câu trả lời cũ đó vẫn có vẻ phù hợp, phải không?
VonC

1
@VonC: Chắc chắn rồi. Tôi chỉ không chắc chắn về "chu kỳ CPU không cần thiết" và "trước khi hết bộ nhớ" kích hoạt bộ sưu tập rác. Xem stackoverflow.com/q/20230626/693207 . Có lẽ bạn có thể làm sáng tỏ một số?
Jürgen Thelen

1
@Omar Tôi đã chỉnh sửa câu trả lời: Trang người đàn ông chưa đặt từ năm 2009 (tôi đã liên kết với phiên bản 2009) không bao gồm một phần không còn xuất hiện trong phiên bản hiện tại của cùng trang đó.
VonC

48

unsetkhông thực sự là một chức năng, mà là một cấu trúc ngôn ngữ . Nó không phải là một cuộc gọi chức năng hơn một returnhoặc một include.

Ngoài các vấn đề về hiệu năng, việc sử dụng unsetlàm cho ý định mã của bạn rõ ràng hơn nhiều.


Đó là lý do tại sao tôi luôn sử dụng chúng, cá nhân tôi nghĩ rằng chúng trông tốt hơn $ var = null. Nhân tiện, tôi đã luôn sử dụng NULL full caps ... nhưng bây giờ tôi không biết tại sao?
alex

1
@VonC: Vâng, tôi đã hiểu điều đó, nhưng tại sao bạn có thể sử dụng chữ thường đúng, sai và null?
alex

3
@alex, bạn có thể sắp xếp làm điều đó với unset. Ví dụ: "$ test = 4; (bỏ đặt) $ test;" - lạ nhưng đúng và nó trả về giá trị của $ test trước khi bỏ đặt nó. Bất kể, hướng dẫn PHP không xác nhận rằng đó là một cấu trúc ngôn ngữ.
thomasrutter

5
@alex: PSR-2 yêu cầu chữ thường cho tất cả các từ khóa.
Tgr

2
@alex - Từ khóa PHP không phân biệt chữ hoa chữ thường; bạn cũng có thể đánh vần unsetnhư UnSeT, ví dụ. Cộng đồng đã giải quyết tất cả các chữ thường như một vấn đề về phong cách, nhưng các vỏ bọc khác vẫn hoạt động.
Đánh dấu

35

Bằng cách thực hiện unset () trên một biến, về cơ bản bạn đã đánh dấu biến đó cho 'bộ sưu tập rác' (PHP không thực sự có một biến, nhưng ví dụ là vì lợi ích) nên bộ nhớ không có sẵn ngay lập tức. Biến không còn chứa dữ liệu, nhưng ngăn xếp vẫn ở kích thước lớn hơn. Thực hiện phương thức null làm giảm dữ liệu và thu nhỏ bộ nhớ stack gần như ngay lập tức.

Điều này đã được từ kinh nghiệm cá nhân và những người khác là tốt. Xem các bình luận của hàm unset () tại đây .

Cá nhân tôi sử dụng unset () giữa các lần lặp trong một vòng lặp để tôi không phải có độ trễ của ngăn xếp có kích thước yo-yo. Dữ liệu đã biến mất, nhưng dấu chân vẫn còn. Ở lần lặp lại tiếp theo, bộ nhớ đã được php sử dụng và do đó, nhanh hơn để khởi tạo biến tiếp theo.


15
Đặt một cái gì đó thành NULL có thể có ích nếu bộ nhớ cần để giữ giá trị NULL nhỏ hơn mức cần thiết để giữ bất kỳ giá trị nào mà nó đã giữ trước đó. Ví dụ, một chuỗi dài. Nếu chuỗi không phải là hằng số và số tham chiếu của nó giảm xuống 0, thì bộ nhớ đó sẽ được giải phóng. Unset là sạch hơn - nó không còn duy trì một tài liệu tham khảo. Bạn phải chờ thu gom rác, nhưng an toàn khi coi nó là không chiếm bộ nhớ, vì điều kiện bộ nhớ thấp sẽ kích hoạt thu gom rác.
thomasrutter

chúng ta không thể sử dụng cả hai? bằng null rồi unset?
Nabeel Khan

2
@NabeelKhan Tôi sẽ đề nghị sử dụng unset () bên trong các vòng lặp và sau đó vô hiệu hóa nó khi bạn thoát khỏi vòng lặp. Mặt khác, có một tác động hiệu suất để làm cả hai bên trong vòng lặp. Nếu bạn không sử dụng các vòng lặp, thì hãy vô hiệu hóa vì nó đã thực hiện logic unset () phía sau hậu trường.
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";
?>

Theo đó, có vẻ như "= null" nhanh hơn.

Kết quả 5,4 PHP:

  • mất 0,88389301300049 giây
  • mất 2.1757180690765 giây

Kết quả PHP 5.3:

  • mất 1,7235369682312 giây
  • mất 2.9490959644318 giây

Kết quả PHP 5.2:

  • mất 3.0069220066071 giây
  • mất 4.7002630233765 giây

Kết quả PHP 5.1:

  • mất 2.6272349357605 giây
  • mất 5.0403649806976 giây

Mọi thứ bắt đầu khác với PHP 5.0 và 4.4.

5.0:

  • mất 10.038941144943 giây
  • mất 7.0874409675598 giây

4,4:

  • mất 7.5352551937103 giây
  • mất 6,6245851516724 giây

Hãy nhớ rằng microtime (đúng) không hoạt động trong PHP 4.4 vì vậy tôi đã phải sử dụng ví dụ microtime_float được đưa ra trong php.net/microtime / Ví dụ # 1.


7
Tôi nghĩ rằng bài kiểm tra của bạn là thiếu sót. Vòng lặp đầu tiên là sự gán lại đơn giản và vòng lặp thứ hai sẽ phá hủy và tạo lại cùng một biểu tượng. Nếu kiểm tra được làm lại với một mảng unsetlà nhanh hơn. Tôi có một bài kiểm tra mà sau đó kiểm tra sự tồn tại trong unsettrường hợp. Trong thử nghiệm đó, cài đặt nullnhanh hơn một chút. Kiểm tra: pastebin.com/fUe57C51
Knyri

4
@ansur, luôn gọi gc_collect_cyclestrước khi bắt đầu hẹn giờ để có kết quả chính xác hơn.
Pacerier

@knyri bạn có thể vui lòng liên kết đến đó không?
Nabeel Khan

@NabeelKhan Tôi không còn có kết quả của bài kiểm tra đó; nhưng có một liên kết đến mã kiểm tra trong bình luận trước đây của tôi.
Knyri

19

Nó làm cho một sự khác biệt với các yếu tố mảng.

Xem xét ví dụ này

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

Ở đây, khóa 'kiểm tra' vẫn tồn tại. Tuy nhiên, trong ví dụ này

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

chìa khóa không còn tồn tại


18

Nó hoạt động theo một cách khác cho các biến được sao chép bởi tham chiếu:

$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
Tôi đã mã hóa php một vài năm nay và chưa bao giờ thấy "&" về việc tham chiếu var gốc. Cảm ơn + 1 :)
Chris

1
$ a = 78; $ b = $ a; bỏ đặt ($ a); var_dump ($ b); // 78; var_dump ($ a); // Biến không xác định: a
zloctb

13

Về các đối tượng, đặc biệt là trong kịch bản tải chậm, người ta nên xem bộ thu gom rác đang chạy trong các chu kỳ CPU nhàn rỗi, vì vậy, giả sử bạn đang gặp rắc rối khi nhiều đối tượng đang tải hình phạt thời gian nhỏ sẽ giải quyết việc giải phóng bộ nhớ.

Sử dụng time_nanos ngủ để kích hoạt GC để thu thập bộ nhớ. Đặt biến thành null là mong muốn.

Đã thử nghiệm trên máy chủ sản xuất, ban đầu công việc tiêu tốn 50MB và sau đó bị dừng lại. Sau khi nano được sử dụng, 14MB là mức tiêu thụ bộ nhớ không đổi.

Mọi người nên nói điều này phụ thuộc vào hành vi của GC có thể thay đổi từ phiên bản PHP sang phiên bản. Nhưng nó hoạt động tốt trên PHP 5.3.

ví dụ. mẫu này (mã được lấy từ nguồn cấp dữ liệu google VirtueMart2)

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

Tôi vẫn nghi ngờ về điều này, nhưng tôi đã thử nó ở kịch bản của mình và tôi đang sử dụng xdebug để biết nó sẽ ảnh hưởng đến việc sử dụng bộ nhớ ứng dụng của tôi như thế nào. Kịch bản được đặt trên chức năng của tôi như thế này:

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

Và tôi thêm unset ngay trước returnmã và nó cho tôi: 160200 sau đó tôi cố gắng thay đổi nó $sql = NULLvà nó cho tôi: 160224 :)

Nhưng có một cái gì đó độc đáo trên so sánh này khi tôi không sử dụng unset () hoặc NULL, xdebug cung cấp cho tôi 160144 làm bộ nhớ sử dụng

Vì vậy, tôi nghĩ việc đưa ra dòng để sử dụng unset () hoặc NULL sẽ thêm quy trình vào ứng dụng của bạn và sẽ tốt hơn nếu giữ nguyên gốc với mã của bạn và giảm biến mà bạn đang sử dụng hiệu quả nhất có thể.

Sửa lỗi cho tôi nếu tôi sai, cảm ơn


Tôi nghĩ rằng trong khi bạn trả về mục $ data [0], toàn bộ mảng được tham chiếu / nhưng đó chỉ là giả thuyết. Cố gắng sao chép $ data [0] sang biến cục bộ, đặt mảng thành null và trả về biến cục bộ. Nền tốt là ở đây tuxradar.com/prrealphp/18/1/11 và ofc. php.net/manual/en/features.gc.php
OSP

2

Tôi đã tạo một thử nghiệm hiệu năng mới cho unset=null, vì như đã đề cập trong các bình luận, văn bản ở đây có lỗi (việc tạo lại các yếu tố). Tôi đã sử dụng mảng, như bạn thấy nó không quan trọng bây giờ.

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

Nhưng tôi chỉ có thể kiểm tra nó trên máy chủ PHP 5.5.9, ở đây có kết quả: - mất 4.4571571350098 giây - mất 4.4425978660583 giây

Tôi thích unsetvì lý do dễ đọc.


2

PHP 7 đã làm việc với các vấn đề quản lý bộ nhớ như vậy và việc sử dụng giảm đến mức tối thiểu.

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

?>

Outpu PHP 7.1:

mất 0.16778993606567 giây mất 0.16660201203918 giây


1

unsetmã nếu không giải phóng bộ nhớ ngay lập tức vẫn rất hữu ích và sẽ là một cách thực hành tốt mỗi khi chúng ta chuyển các bước mã trước khi thoát khỏi một phương thức. lưu ý nó không phải là về giải phóng bộ nhớ ngay lập tức. Bộ nhớ tức thì dành cho CPU, còn bộ nhớ thứ cấp là RAM.

và điều này cũng khắc phục về việc ngăn chặn rò rỉ bộ nhớ.

vui lòng xem liên kết này http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

Tôi đã sử dụng unset trong một thời gian dài bây giờ.

thực hành tốt hơn như thế này trong mã để ngay lập tức bỏ đặt tất cả các biến đã được sử dụng như là mảng.

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

just unset($data);để giải phóng tất cả việc sử dụng biến.

vui lòng xem chủ đề liên quan để bỏ đặt

Làm thế nào quan trọng là bỏ đặt các biến trong PHP?

[bọ cánh cứng]


1

Đối với bản ghi và không bao gồm thời gian cần thiết:

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

Nó trở lại

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

Kết luận, cả null và unset bộ nhớ trống như mong đợi (không chỉ ở cuối thực thi). Ngoài ra, việc gán lại một biến giữ giá trị hai lần tại một số điểm (520216 so với 438352)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.