สิ่งที่ดีกว่าในการเพิ่มหน่วยความจำด้วย PHP: unset () หรือ $ var = null


244

ฉันรู้ว่าอันที่สองหลีกเลี่ยงค่าใช้จ่ายในการเรียกฟังก์ชั่น ( อัปเดตเป็นโครงสร้างภาษาจริง) แต่มันน่าสนใจที่จะทราบว่าหนึ่งดีกว่าอีกอันหนึ่ง ฉันใช้unset()สำหรับการเขียนโค้ดส่วนใหญ่ แต่เมื่อเร็ว ๆ นี้ฉันได้ดูคลาสที่น่านับถือสองสามตัวที่พบจากเน็ตที่ใช้$var = nullแทน

มีคนที่ชอบและเหตุผลคืออะไร?

คำตอบ:


234

มันถูกกล่าวถึงในหน้าunset คู่มือในปี 2009 :

unset()ทำในสิ่งที่ชื่อพูด - ตั้งค่าตัวแปร มันไม่ได้บังคับให้หน่วยความจำทันทีพ้น ตัวเก็บรวบรวมขยะของ PHP จะทำเมื่อเห็นว่าเหมาะสม - โดยเจตนาโดยเร็วเนื่องจากไม่จำเป็นต้องใช้รอบ CPU เหล่านั้นหรือก่อนที่สคริปต์จะหมดหน่วยความจำสิ่งใดก็ตามที่เกิดขึ้นก่อน

หากคุณกำลังทำเช่น$whatever = null;นั้นคุณกำลังเขียนข้อมูลของตัวแปรอีกครั้ง คุณอาจได้รับหน่วยความจำอิสระ / หดเร็วขึ้น แต่มันอาจขโมยรอบ CPU จากรหัสที่ต้องการได้เร็วขึ้นส่งผลให้เวลาในการดำเนินการโดยรวมนานขึ้น

(ตั้งแต่ปี 2013 unsetหน้า man นั้นไม่รวมส่วนนั้นอีกต่อไป)

โปรดทราบว่าจนกว่า 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

1
@Omar ฉันได้แก้ไขคำตอบแล้ว: หน้า man unset ตั้งแต่ปี 2009 (ฉันเชื่อมโยงกับเวอร์ชั่น 2009) รวมถึงส่วนที่ไม่มีอยู่ในเวอร์ชันปัจจุบันของหน้าเดียวกันอีกต่อไป
VonC

48

unsetไม่จริงฟังก์ชั่น แต่ภาษาสร้าง มันไม่ขึ้นเรียกฟังก์ชันกว่าหรือreturninclude

นอกเหนือจากปัญหาด้านประสิทธิภาพการใช้unsetทำให้เจตนาของโค้ดของคุณชัดเจนยิ่งขึ้น


นั่นเป็นเหตุผลที่ฉันมักจะใช้พวกเขาโดยส่วนตัวฉันคิดว่าพวกเขาดูดีกว่า $ var = null อย่างไรก็ตามฉันเคยใช้ตัวพิมพ์ใหญ่แบบเต็มของ NULL เสมอ แต่ตอนนี้ฉันไม่รู้ว่าทำไม
alex

1
@VonC: ใช่ฉันคิดว่า แต่ทำไมคุณสามารถใช้ตัวพิมพ์เล็กจริงเท็จและ null ได้
alex

3
@alex คุณสามารถเรียงลำดับได้โดยไม่ต้องตั้งค่า ตัวอย่างเช่น "$ test = 4; (unset) $ test;" - แปลก แต่จริงและมันส่งคืนค่า $ ทดสอบก่อนที่จะยกเลิกการตั้งค่า ไม่ว่าคู่มือ PHP จะยืนยันว่าเป็นโครงสร้างภาษา
thomasrutter

5
@alex: PSR-2 ต้องการตัวพิมพ์เล็กสำหรับคำหลักทั้งหมด
Tgr

2
@alex - คำหลัก PHP เป็นแบบตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ คุณยังสามารถสะกดunsetเป็นUnSeTเช่น ชุมชนได้ตัดสินตัวพิมพ์เล็กทั้งหมดตามสไตล์ แต่ตัวเรือนอื่นยังคงใช้งานได้
Mark Reed

35

ด้วยการทำ unset () บนตัวแปรคุณได้ทำเครื่องหมายตัวแปรสำหรับ 'garbage collection' (PHP ไม่มีจริง ๆ แต่สำหรับประโยชน์ของตัวอย่าง) ดังนั้นหน่วยความจำจึงไม่สามารถใช้งานได้ทันที ตัวแปรไม่เป็นที่เก็บข้อมูลอีกต่อไป แต่สแต็กยังคงอยู่ที่ขนาดที่ใหญ่กว่า การทำเมธอด null ปล่อยข้อมูลและลดขนาดหน่วยความจำสแต็กเกือบจะในทันที

นี่เป็นประสบการณ์ส่วนตัวและจากคนอื่นเช่นกัน ดูความคิดเห็นของไม่มีการตั้งค่าที่ () ทำงานที่นี่

ฉันใช้ unset () ระหว่างการวนซ้ำเป็นการส่วนตัวเพื่อที่ฉันจะได้ไม่ต้องมีการหน่วงเวลาของสแต็กที่มีขนาดเท่าที่คุณต้องการ ข้อมูลหายไป แต่รอยเท้ายังคงอยู่ ในการทำซ้ำครั้งต่อไปหน่วยความจำนั้นถูกใช้งานโดย php ดังนั้นจึงเร็วกว่าที่จะเริ่มต้นตัวแปรถัดไป


15
การตั้งค่าบางอย่างให้เป็น NULL อาจเป็นประโยชน์หากหน่วยความจำที่ต้องการเก็บค่า NULL นั้นน้อยกว่าที่จำเป็นในการเก็บค่าใด ๆ ที่เคยถือไว้ก่อนหน้านี้ ตัวอย่างเช่นสายยาว หากสตริงไม่ใช่ค่าคงที่และจำนวนการอ้างอิงลดลงเป็นศูนย์แสดงว่าหน่วยความจำนั้นควรถูกปล่อยให้เป็นอิสระ Unset is cleaner - ไม่มีการอ้างอิงอีกต่อไป คุณต้องรอการรวบรวมขยะ แต่ก็ปลอดภัยที่จะถือว่าไม่มีหน่วยความจำเพราะเงื่อนไขหน่วยความจำต่ำจะทำให้เกิดการรวบรวมขยะ
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 (จริง) ไม่ทำงานใน PHP 4.4 ดังนั้นฉันต้องใช้ตัวอย่าง microtime_float ที่ให้ไว้ใน php.net/microtime / ตัวอย่าง # 1


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 ($ ข); // 78; var_dump ($ a); // ตัวแปรที่ไม่ได้กำหนด: a
zloctb

13

เกี่ยวกับวัตถุโดยเฉพาะอย่างยิ่งในสถานการณ์ที่ขี้เกียจโหลดอย่างใดอย่างหนึ่งควรพิจารณาตัวรวบรวมขยะกำลังทำงานในรอบ CPU ว่างดังนั้นสันนิษฐานว่าคุณกำลังมีปัญหาเมื่อวัตถุจำนวนมากกำลังโหลดบทลงโทษเล็ก ๆ น้อย ๆ จะแก้ปัญหาการปลดปล่อยหน่วยความจำ

ใช้ time_nanosleep เพื่อเปิดใช้งาน GC เพื่อรวบรวมหน่วยความจำ การตั้งค่าตัวแปรเป็นโมฆะเป็นสิ่งที่ต้องการ

ทดสอบกับเซิร์ฟเวอร์ที่ใช้งานจริง แต่เดิมงานนั้นใช้งาน 50MB แล้วจึงหยุดทำงาน หลังจากใช้ nanosleep 14MB จะเป็นการสิ้นเปลืองหน่วยความจำอย่างต่อเนื่อง

หนึ่งควรกล่าวว่านี้ขึ้นอยู่กับพฤติกรรมของ GC ซึ่งอาจเปลี่ยนจากรุ่น PHP เป็นรุ่น แต่มันใช้งานได้กับ 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;
}

และฉันเพิ่ม unset ก่อนreturnรหัสและให้ฉัน: 160200 จากนั้นฉันพยายามเปลี่ยนด้วย$sql = NULLและให้ฉัน: 160224 :)

แต่มีบางอย่างที่ไม่เหมือนใครในการเปรียบเทียบนี้เมื่อฉันไม่ได้ใช้ unset () หรือ NULL, xdebug ให้ 160144 ฉันเป็นการใช้หน่วยความจำ

ดังนั้นฉันคิดว่าการให้บรรทัดใช้ unset () หรือ NULL จะเพิ่มกระบวนการลงในแอปพลิเคชันของคุณและมันจะเป็นการดีกว่าถ้าคุณเริ่มต้นด้วยรหัสของคุณและลดตัวแปรที่คุณใช้ให้มีประสิทธิภาพมากที่สุด

ถูกต้องฉันถ้าฉันผิดขอบคุณ


ฉันคิดว่าในขณะที่คุณส่งคืนข้อมูล $ data [0] รายการทั้งหมดจะถูกอ้างอิง / แต่เป็นเพียงสมมติฐาน ลองคัดลอก $ data [0] ไปที่ตัวแปรโลคอลตั้งค่าอาร์เรย์เป็น null และส่งกลับตัวแปรโลคอล พื้นหลังที่ดีอยู่ที่นี่tuxradar.com/practicalphp/18/1/11และ ofc php.net/manual/th/features.gc.php
OSP

2

ฉันสร้างการทดสอบประสิทธิภาพใหม่สำหรับunsetและ=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

ฉันใช้ unset เป็นเวลานานแล้ว

ควรฝึกให้ดีขึ้นเช่นนี้ในโค้ดเพื่อยกเลิกการตั้งค่าตัวแปรทั้งหมดที่ถูกใช้เป็นอาร์เรย์แล้ว

$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.

สรุปทั้งหน่วยความจำว่างและไม่ได้ตั้งค่าตามที่คาดไว้ (ไม่เพียง แต่เมื่อสิ้นสุดการประมวลผล) นอกจากนี้การมอบหมายตัวแปรให้เก็บค่าสองครั้งในบางจุด (520216 เทียบกับ 438352)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.