บทนำสู่อ็อบเจ็กต์และการอ้างอิงในหน่วยความจำ PHP

เผยแพร่แล้ว: 2022-03-11

ขั้นแรก ฉันร่างบทความนี้ในขณะที่ศึกษาการรับรอง PHP เพื่อพยายามทำความเข้าใจวิธีที่ PHP จัดการตัวแปรและอ็อบเจกต์ในหน่วยความจำให้ดีขึ้น หลังจากการค้นคว้าหลายครั้ง ฉันตระหนักว่าการค้นหาคำตอบสำหรับคำถามของฉันนั้นไม่ง่าย ดังนั้นเมื่อฉันทำเสร็จแล้ว ฉันจึงตัดสินใจจัดทำเอกสารข้อมูลเพื่อให้ผู้คนสามารถค้นหาคำตอบทั้งหมดได้ในที่เดียว

ในบทความนี้ ผมจะพูดถึงวิธีควบคุมการอ้างอิงอ็อบเจกต์และตัวแปรในหน่วยความจำ เนื่องจากเป็นประเด็นที่อาจทำให้เกิดการอภิปรายและความคิดเห็นที่แตกต่างกัน คำถามหนึ่งที่ต้องไตร่ตรองคือ: "โดยค่าเริ่มต้น วัตถุถูกส่งผ่านโดยการอ้างอิงหรือโดยการคัดลอกใน PHP หรือไม่" ฉันจะพูดถึงก่อนว่าการอ้างอิงใดที่ ไม่ได้ อยู่ใน PHP; ประการที่สอง ฉันจะพูดถึงสิ่งที่พวก เขา และสุดท้าย ฉันจะตรวจสอบว่าตัวรวบรวมขยะทำงานอย่างไรใน PHP

PHP สร้างวัตถุในหน่วยความจำอย่างไรเมื่อดำเนินการคำสั่งเช่น $a = new Foo(); ? ปัจจุบันหน่วยความจำไม่แพงและจำกัดทรัพยากรอย่างที่เคยเป็นมา อย่างไรก็ตาม ยังคงเป็นสิ่งสำคัญสำหรับนักพัฒนา PHP ที่ดีที่จะรู้และเข้าใจว่าตัวแปรและอ็อบเจ็กต์ได้รับการจัดการภายในอย่างไรระหว่างการดำเนินการของแอปพลิเคชัน

ออบเจ็กต์และการอ้างอิงในหน่วยความจำ PHP และการรวบรวมขยะ PHP

วัตถุและการอ้างอิงใน PHP

หลายคนบอกว่า ในหนังสือ PHP และออนไลน์—ว่าอ็อบเจกต์ใน PHP นั้นส่งผ่านโดยการอ้างอิงตามค่าเริ่มต้น บางคนบอกว่าวัตถุใน PHP ได้รับการจัดสรรโดยการคัดลอก ในการหาว่าคำสั่งใดถูกต้อง ก่อนอื่นเราต้องวิเคราะห์ว่าอะไรคือ (และอะไรไม่ใช่) ข้อมูลอ้างอิงใน PHP

อะไร ไม่ อ้างอิงใน PHP?

สำคัญกว่าการรู้ว่าการอ้างอิงใดใน PHP คือการรู้ว่า ไม่ใช่ สิ่งอ้างอิง ใน PHP การอ้างอิงไม่ใช่ตัวชี้แบบ C คุณไม่สามารถดำเนินการเลขคณิตด้วยการอ้างอิงได้เหมือนกับที่คุณสามารถทำได้ด้วยพอยน์เตอร์ C ทำไม? เนื่องจากไม่เหมือนกับในภาษา C การอ้างอิง PHP ไม่ใช่ที่อยู่หน่วยความจำจริงๆ เนื่องจากไม่ใช่ตัวเลขที่ระบุตำแหน่งหน่วยความจำ แต่แล้วการอ้างอิง คือ อะไร?

การอ้างอิงใน PHP คืออะไร ?

ใน PHP การอ้างอิงคือ "นามแฝง" ที่อนุญาตให้สองตัวแปรที่แตกต่างกันอ่านและเขียนค่าเดียว กล่าวอีกนัยหนึ่ง สิ่งเหล่านี้เป็นกลไกที่อนุญาตให้เข้าถึงค่าเดียวกันจากตัวแปรที่มีชื่อต่างกัน เพื่อให้ทำงานราวกับว่าเป็นตัวแปรเดียวกัน โปรดทราบว่าใน PHP ชื่อตัวแปรและเนื้อหาของตัวแปรเป็นสองสิ่งที่แตกต่างกันโดยสิ้นเชิง ซึ่งเชื่อมโยงในสิ่งที่เรียกว่า "ตารางสัญลักษณ์" ดังนั้น เมื่อเราสร้างการอ้างอิง มันจะเพิ่มนามแฝงสำหรับตัวแปรนั้นในตารางสัญลักษณ์ สมมติว่าเรามีรหัสต่อไปนี้:

 $a = new Foo();

เมื่อดำเนินการคำสั่งข้างต้น ตัวแปร $a จะถูกสร้างขึ้นในหน่วยความจำ วัตถุประเภท Foo จะถูกสร้างขึ้นในหน่วยความจำ และรายการจะถูกเพิ่มลงในตารางสัญลักษณ์ซึ่งบ่งชี้ว่าตัวแปร $a “การอ้างอิง” (หรือเกี่ยวข้องกับ หรือชี้ไปที่หรือสิ่งที่คุณต้องการเรียก) วัตถุ Foo แต่ไม่ใช่ ตัวชี้ ไปยังวัตถุนั้นโดยส่วนตัว ตามแนวคิดแล้ว เรามีบางอย่างเช่นภาพประกอบนี้:

การอ้างอิงวัตถุในหน่วยความจำ PHP

แบบทดสอบป๊อป: จะเกิดอะไรขึ้นถ้าเราดำเนินการนี้

 $b = $a;

ไม่ใช่ว่า $b กลายเป็นข้อมูลอ้างอิงของ $a ; เราไม่สามารถพูดได้ว่า $b เป็นสำเนาของ $a สิ่งที่เกิดขึ้นจริงคือเราได้สร้างตัวแปร $b ​​ใหม่ในหน่วยความจำแล้วเพิ่มรายการใหม่ในตารางสัญลักษณ์ที่ระบุว่าตัวแปร $b ​​ยังอ้างอิงวัตถุ Foo เดียวกันกับที่ $a ทำ ดังนั้น ทางสายตา เรามีบางสิ่งที่คล้ายกับที่แสดงในภาพประกอบนี้:

การอ้างอิงวัตถุในหน่วยความจำ PHP

ตอนนี้ถ้าเราดำเนินการ:

 $c = &$a;

เราจะสร้างตัวแปรตัวที่สาม $c ในหน่วยความจำ แต่ ไม่ใช่ รายการใหม่ในตารางสัญลักษณ์สำหรับ $c ในตารางสัญลักษณ์ จะมีการบันทึกว่า $c เป็นนามแฝงสำหรับ $a ดังนั้นจะมีการทำงานเหมือนกัน แต่ $c ไม่ใช่ตัวชี้ไปยัง $a แตกต่างจาก C ซึ่งสร้างสิ่งที่เรียกว่า pointers to pointers เพื่อให้เห็นภาพ เรามีบางสิ่งที่คล้ายกับที่แสดงในภาพประกอบนี้:

ไดอะแกรมของตัวแปรและนามแฝง

ทันทีที่เราต้องการแก้ไขค่าของตัวแปรสามตัวเหล่านี้ (เช่น เขียนค่าใหม่) PHP จะต้องสร้างโครงสร้าง z_val ใหม่ในหน่วยความจำเพื่อแยกเนื้อหาของตัวแปร $b ​​และคู่ $a / $c เพื่อให้สามารถแก้ไขได้โดยอิสระโดยไม่กระทบต่อมูลค่าของอีกฝ่าย ดังนั้น หากเราเพิ่มบรรทัดต่อไปนี้ในสคริปต์ก่อนหน้า:

 $b = new Bar();

ในความทรงจำ เราจะมีสถานการณ์ดังแสดงในภาพประกอบต่อไปนี้:

การแสดงกราฟิกของสถานการณ์ที่อธิบายไว้ข้างต้น

ตอนนี้ มาลองพิจารณาตัวอย่างที่สมบูรณ์กว่านี้:

 <?php class myClass { public $var; function __construct() { $this->var = 1; } function inc() { return ++$this->var; } } $a = new myClass(); // $a "references" a Foo object $b = $a; //b also references the same Foo object as a //($a) == ($b) == <id> of Foo object, but a and b are different entries in symbols table echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); $c = &$a; //$c is an alias of $a //($a, $c) == <id> of Foo object, c is an alias of a in the symbols table echo "$c = ";var_dump($c); $a = NULL; //The entry in the symbols table which links "$a" with Foo object is removed //Since that entry was removed, $c is not related to Foo anymore //Anyway, Foo still exists in memory and it is still linked by $b echo "$a = ";var_dump($a); echo "$b = ";var_dump($b); echo "$c = ";var_dump($c); echo "$b->var: ".$b->inc(); echo "$b->var: ".$b->inc(); $b = NULL; //The entry in the symbols table which links "$b" with the Foo object is removed //There are no more entries in the symbols table linked to Foo, //So, Foo is not referenced anymore and can be deleted by the garbage collector echo "$b = ";var_dump($b);

ผลลัพธ์ที่ได้จากการใช้งานสคริปต์ด้านบนคือ:

 $a = object(myClass)#1 (1) { ["var"]=> int(1) } $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = object(myClass)#1 (1) { ["var"]=> int(1) } $a = NULL $b = object(myClass)#1 (1) { ["var"]=> int(1) } $c = NULL $b->var: 2 $b->var: 3 $b = NULL

PHP Garbage Collection

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

ปิดความคิด

ฉันหวังว่าฉันได้ชี้แจงเล็กน้อยว่า PHP จัดการกับวัตถุและตัวแปรในหน่วยความจำอย่างไร และมัน "เลือก" วัตถุอย่างไรที่ควรลบโดยตัวรวบรวมขยะ PHP

ตอนนี้คุณเข้าใจวิธีที่ PHP จัดการตัวแปรและอ็อบเจ็กต์ในหน่วยความจำภายในแล้ว คว้าแล็ปท็อปของคุณแล้วเริ่มทดลองโค้ดเพื่อพิสูจน์สิ่งที่คุณได้เรียนรู้ ลองเล่นกับตัวแปรและข้อมูลอ้างอิง นอกจากนี้ ให้ทดลองว่าการเปลี่ยนแปลงค่าของตัวแปรอาจส่งผลต่อค่าของตัวแปรอื่นที่อ้างอิงได้อย่างไร นี่คือคำถามสำหรับคุณ: ค่าของ $a และ $b จะเป็นอย่างไรหลังจากรันโค้ดด้านล่าง

 $a = '1'; $b = &$a; $b = "2$b";

หากคุณสนใจที่จะอ่านเพิ่มเติมเกี่ยวกับคุณลักษณะด้านประสิทธิภาพของ PHP โปรดดูโพสต์นี้โดยเพื่อน Toptaler Vilson Duka