คอลเล็กชันขยะ JavaScript คืออะไร


297

คอลเล็กชันขยะ JavaScript คืออะไร สิ่งสำคัญสำหรับผู้เขียนโปรแกรมเว็บคือต้องเข้าใจเกี่ยวกับการรวบรวมขยะ JavaScript เพื่อที่จะเขียนโค้ดที่ดีกว่า



คำตอบ:


192

Eric Lippert เขียนโพสต์บล็อกที่มีรายละเอียดเกี่ยวกับเรื่องนี้มาระยะหนึ่งแล้ว (เปรียบเทียบเพิ่มเติมกับVBScript ) อย่างแม่นยำยิ่งขึ้นเขาเขียนเกี่ยวกับJScriptซึ่งเป็นการนำ ECMAScript ไปใช้ของ Microsoft แม้ว่าจะคล้ายกันมากกับ JavaScript ฉันคิดว่าคุณสามารถสันนิษฐานได้ว่าพฤติกรรมส่วนใหญ่จะเหมือนกันสำหรับเอนจิ้น JavaScript ของ Internet Explorer แน่นอนการใช้งานจะแตกต่างกันไปจากเบราว์เซอร์เบราว์เซอร์ แต่ฉันคิดว่าคุณสามารถใช้หลักการทั่วไปจำนวนหนึ่งและนำไปใช้กับเบราว์เซอร์อื่น ๆ

อ้างจากหน้านั้น:

JScript ใช้ตัวรวบรวมขยะที่ไม่ใช่เครื่องหมายและกวาด มันทำงานได้เช่นนี้:

  • ตัวแปรทุกตัวที่ "อยู่ในขอบเขต" เรียกว่า "เกมกินขยะ" คนเก็บขยะอาจอ้างถึงจำนวนวัตถุสตริงหรืออะไรก็ได้ เราเก็บรายชื่อของคนเก็บขยะ - ตัวแปรจะถูกย้ายไปยังรายการของคนเก็บขยะเมื่อพวกเขาเข้ามาในขอบเขตและออกจากรายการขยะเมื่อพวกเขาออกจากขอบเขต

  • ทุก ๆ ครั้งที่ตัวรวบรวมขยะจะทำงาน ก่อนอื่นให้วาง "เครื่องหมาย" ไว้ในทุกวัตถุตัวแปรสตริง ฯลฯ - หน่วยความจำทั้งหมดที่ติดตามโดย GC (JScript ใช้โครงสร้างข้อมูลที่หลากหลายภายในและมีบิตที่ไม่ได้ใช้เป็นจำนวนมากในโครงสร้างนั้นดังนั้นเราเพิ่งตั้งหนึ่งในนั้น)

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

  • ณ จุดนี้เรารู้ว่าหน่วยความจำทั้งหมดที่ทำเครื่องหมายไว้ยังถูกจัดสรรหน่วยความจำซึ่งไม่สามารถเข้าถึงได้โดยเส้นทางใด ๆ จากตัวแปรใด ๆ ในขอบเขต วัตถุเหล่านั้นทั้งหมดได้รับคำสั่งให้ทำลายตัวเองซึ่งทำลายการอ้างอิงแบบวงกลมใด ๆ

วัตถุประสงค์หลักของการรวบรวมขยะคือเพื่อให้โปรแกรมเมอร์ไม่ต้องกังวลเกี่ยวกับการจัดการหน่วยความจำของวัตถุที่พวกเขาสร้างและใช้งาน แต่แน่นอนว่าไม่มีการหลีกเลี่ยงบางครั้ง - มันเป็นประโยชน์เสมอที่จะมีความคิดคร่าวๆ .

บันทึกประวัติ:การแก้ไขคำตอบก่อนหน้านี้มีการอ้างอิงที่ไม่ถูกต้องไปยังdeleteผู้ประกอบการ ใน JavaScript ประกอบการเอาทรัพย์สินจากวัตถุและเป็นเครือที่แตกต่างกันไปใน C / C ++deletedelete


27
คู่มือ Apple มีข้อบกพร่อง: autor ใช้deleteอย่างไม่ถูกต้อง เช่นในตัวอย่างแรกแทนที่จะเป็นdelete fooคุณควรลบตัวฟังเหตุการณ์ออกก่อนwindow.removeEventListener()แล้วจึงใช้foo = nullเพื่อเขียนทับตัวแปร ใน IE delete window.foo(แต่ไม่ใช่delete foo) ก็จะได้ผลเช่นกันถ้าfooเป็นโลก แต่ถึงอย่างนั้นมันก็ไม่ได้อยู่ใน FF หรือ Opera
Christoph

3
พึงระวังว่าบทความของ Eric ควรได้รับการพิจารณาว่า "เพื่อจุดประสงค์ทางประวัติศาสตร์เท่านั้น" แต่มันก็ยังให้ข้อมูล
Peter Ivan

2
หมายเหตุ - IE 6 และ 7 ห้ามใช้ตัวรวบรวมข้อมูลขยะที่ไม่ใช่เครื่องหมายและตัวกวาด พวกเขาใช้ตัวรวบรวมขยะนับอ้างอิงอย่างง่ายซึ่งมีความเสี่ยงต่อปัญหาการอ้างอิงแบบวงกลมกับการรวบรวมขยะ
Doug

1
ECMAScript ของdeleteเป็นผู้ดำเนินเอก (การแสดงออก) ไม่ใช่คำสั่ง (เช่น: delete 0, delete 0, delete 3) ดูเหมือนว่าคำสั่งเมื่อแสดงออกโดยคำสั่งการแสดงออก
Hydroper

ใช่แล้วคำตอบในเวลานั้นล้าสมัยไปแล้วในปี 2012 เบราว์เซอร์ที่ทันสมัยใช้เครื่องหมาย / sweep algorthm .. ดังนั้นจึงไม่ได้ขึ้นอยู่กับขอบเขตอีกต่อไป การอ้างอิง: developer.mozilla.org/en-US/docs/Web/JavaScript/
......

52

ระวังการอ้างอิงแบบวงกลมเมื่อเกี่ยวข้องกับวัตถุ DOM:

รูปแบบการรั่วไหลของหน่วยความจำใน JavaScript

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

นี่คือตัวอย่างง่ายๆ:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

การปรับใช้ JS ที่ไร้เดียงสาไม่สามารถรวบรวมได้bigStringตราบใดที่ตัวจัดการเหตุการณ์อยู่ใกล้ มีหลายวิธีในการแก้ปัญหานี้เช่นการตั้งค่าbigString = nullที่ส่วนท้ายของinit()( deleteจะไม่ทำงานกับตัวแปรโลคัลและอาร์กิวเมนต์ของฟังก์ชัน: deleteลบคุณสมบัติออกจากวัตถุและวัตถุตัวแปรไม่สามารถเข้าถึงได้ - ES5 ในโหมดเข้มงวดจะทำให้ReferenceErrorคุณลอง เพื่อลบตัวแปรโลคอล!)

ฉันแนะนำให้หลีกเลี่ยงการปิดที่ไม่จำเป็นให้มากที่สุดถ้าคุณใส่ใจกับการใช้หน่วยความจำ


20
ข้อผิดพลาดในการอ้างอิงแบบวงกลม DOM นั้นเฉพาะกับ JScript - ไม่มีเบราว์เซอร์อื่นที่ทนทุกข์ทรมานนอกจาก IE ในความเป็นจริงฉันค่อนข้างแน่ใจว่าข้อมูลจำเพาะ ECMAScript ระบุไว้อย่างชัดเจนว่า GC จะต้องสามารถจัดการกับวงจรดังกล่าว: - /
olliej

@olliej: ฉันไม่เห็นเอ่ยถึง GC ใด ๆ ในสเป็ค ECMAScript
Janus Troelsen


16

คำพูดที่ดีนำมาจากบล็อก

องค์ประกอบ DOM คือ "ขยะที่เก็บรวบรวม" เช่นเดียวกับองค์ประกอบ JScript ซึ่งหมายความว่าหากคุณสร้างวัตถุภายในองค์ประกอบใดส่วนประกอบหนึ่งแล้วสูญเสียการติดตามวัตถุนั้นในที่สุดมันก็จะถูกกำจัด

ตัวอย่างเช่น:

function makeABigObject() {
var bigArray = new Array(20000);
}

เมื่อคุณเรียกใช้ฟังก์ชันนั้นคอมโพเนนต์ JScript จะสร้างวัตถุ (ชื่อ bigArray) ที่สามารถเข้าถึงได้ภายในฟังก์ชัน แต่ทันทีที่ฟังก์ชั่นกลับมาคุณจะ "สูญเสียการติดตาม" ของ bigArray เพราะไม่มีวิธีการอ้างอิงอีกต่อไป ส่วนประกอบ JScript ตระหนักดีว่าคุณไม่ได้ติดตามมันและดังนั้น bigArray จึงถูกล้างออก - หน่วยความจำของมันถูกเรียกคืน สิ่งของชนิดเดียวกันนั้นใช้งานได้ในคอมโพเนนต์ DOM ถ้าคุณพูดdocument.createElement('div')หรืออะไรทำนองนั้นองค์ประกอบ DOM จะสร้างวัตถุให้คุณ เมื่อคุณสูญเสียการติดตามวัตถุนั้นองค์ประกอบ DOM จะล้างข้อมูลที่เกี่ยวข้อง


13

เพื่อความรู้ของฉันที่ดีที่สุดวัตถุของ JavaScript จะถูกเก็บรวบรวมขยะเป็นระยะเมื่อไม่มีการอ้างอิงถึงวัตถุ มันเป็นสิ่งที่เกิดขึ้นโดยอัตโนมัติ แต่ถ้าคุณต้องการดูเพิ่มเติมเกี่ยวกับวิธีการทำงานในระดับ C ++ มันสมเหตุสมผลที่จะดูซอร์สโค้ดWebKitหรือV8

โดยทั่วไปแล้วคุณไม่จำเป็นต้องคิดเกี่ยวกับเรื่องนี้อย่างไรก็ตามในเบราว์เซอร์รุ่นเก่าเช่น IE 5.5 และ IE 6 รุ่นก่อนหน้าและรุ่นปัจจุบันการปิดจะสร้างการอ้างอิงแบบวงกลมที่เมื่อไม่ได้เลือกจะสิ้นสุดการกินหน่วยความจำ ในกรณีเฉพาะที่ฉันหมายถึงเกี่ยวกับการปิดเป็นเมื่อคุณเพิ่มการอ้างอิง JavaScript ไปยังวัตถุ dom และวัตถุไปยังวัตถุ DOM ที่อ้างถึงกลับไปที่วัตถุ JavaScript โดยทั่วไปจะไม่สามารถรวบรวมได้และในที่สุดจะทำให้ระบบปฏิบัติการไม่เสถียรในการทดสอบแอพที่วนลูปเพื่อสร้างข้อขัดข้อง ในทางปฏิบัติการรั่วไหลเหล่านี้มักจะมีขนาดเล็ก แต่เพื่อให้โค้ดของคุณสะอาดคุณควรลบการอ้างอิง JavaScript ไปยังวัตถุ DOM

โดยปกติแล้วควรใช้คำหลักลบเพื่อยกเลิกการอ้างอิงวัตถุขนาดใหญ่เช่นข้อมูล JSON ที่คุณได้รับกลับมาและทำสิ่งที่คุณต้องทำโดยเฉพาะในการพัฒนาเว็บบนมือถือ นี่ทำให้การกวาดครั้งต่อไปของ GC เพื่อลบวัตถุนั้นออกและเพิ่มหน่วยความจำ


JavaScript -> DOM -> ปัญหาการอ้างอิงแบบวงกลม JavaScript ได้รับการแก้ไขใน IE เวอร์ชันที่ใหม่กว่าหรือไม่ ถ้าเป็นเช่นนั้นตั้งแต่เมื่อไหร่? ฉันคิดว่ามันเป็นสถาปัตยกรรมที่ลึกมากและไม่น่าจะได้รับการแก้ไข คุณมีแหล่งที่มาหรือไม่?
erikkallen

เพียงอย่างเดียว ฉันไม่ได้สังเกตเห็นการรั่วไหลที่บ้าคลั่งใน IE 8 ที่ทำงานในโหมดมาตรฐานไม่ใช่โหมดที่เสีย ฉันจะปรับการตอบสนองของฉัน
ความร้อน Miser

1
@erikkallen: ใช่ข้อผิดพลาด GC ได้รับการแก้ไขใน IE เวอร์ชัน 8 ขึ้นไปเนื่องจากรุ่นเก่าใช้อัลกอริทึมการรวบรวมขยะที่ไร้เดียงสามากซึ่งทำให้ GC เป็นวัตถุที่อ้างอิงกันไม่ได้ ใหม่mark-and-sweepขั้นตอนวิธีการรูปแบบการดูแลนี้
Kumarharsh

6

garbage collection (GC) เป็นรูปแบบของการจัดการหน่วยความจำอัตโนมัติโดยการลบวัตถุที่ไม่ต้องการอีกต่อไป

กระบวนการใด ๆ ที่จัดการกับหน่วยความจำทำตามขั้นตอนเหล่านี้:

1 - จัดสรรพื้นที่หน่วยความจำของคุณที่คุณต้องการ

2 - ทำการประมวลผลบางอย่าง

3 - เพิ่มพื้นที่หน่วยความจำฟรี

มีสองอัลกอริทึมหลักที่ใช้ในการตรวจจับวัตถุที่ไม่ต้องการอีกต่อไป

การรวบรวมขยะนับอ้างอิง : อัลกอริทึมนี้ช่วยลดคำจำกัดความของ "วัตถุไม่จำเป็นอีกต่อไป" เป็น "วัตถุไม่มีวัตถุอื่นที่อ้างอิงถึง" วัตถุจะถูกลบหากไม่มีการอ้างอิงชี้ไปที่มัน

อัลกอริธึม Mark-and-sweep : เชื่อมต่อแต่ละวัตถุกับแหล่งที่มา วัตถุใด ๆ ไม่เชื่อมต่อกับรูทหรือวัตถุอื่น วัตถุนี้จะถูกลบออก

เบราว์เซอร์ที่ทันสมัยที่สุดในปัจจุบันโดยใช้อัลกอริทึมที่สอง


1
และหากต้องการเพิ่มแหล่งที่มาของสิ่งนี้โปรดดู MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/
......

4

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

เอ็นจิ้น JavaScript ทั้งหมดมีตัวรวบรวมขยะของตัวเองและอาจแตกต่างกัน เวลาส่วนใหญ่คุณไม่ต้องจัดการกับพวกเขาเพราะพวกเขาเพียงแค่ทำในสิ่งที่พวกเขาควรจะทำ

การเขียนโค้ดที่ดีขึ้นส่วนใหญ่ขึ้นอยู่กับว่าคุณรู้หลักการเขียนโปรแกรมภาษาและการใช้งานที่เฉพาะเจาะจงอย่างไร


1

คอลเล็กชันขยะ JavaScript คืออะไร

ตรวจสอบนี้

สิ่งสำคัญสำหรับผู้เขียนโปรแกรมเว็บคือต้องเข้าใจเกี่ยวกับการรวบรวมขยะ JavaScript เพื่อที่จะเขียนโค้ดที่ดีกว่า

ใน Javascript คุณไม่สนใจเรื่องการจัดสรรหน่วยความจำและการจัดสรรคืน ปัญหาทั้งหมดถูกเรียกร้องให้ล่าม Javascript การรั่วไหลยังคงเป็นไปได้ใน Javascript แต่เป็นข้อบกพร่องของล่าม หากคุณสนใจในหัวข้อนี้คุณสามารถอ่านเพิ่มเติมได้ที่ www.memorymanagement.org


ระบบการจัดการหน่วยความจำแบบใดในบทความที่คุณลิงค์ไปเป็นระบบที่ใช้โดย JavaScript? "การรั่วไหลยังคงเป็นไปได้ใน Javascript แต่เป็นข้อบกพร่องของล่าม" - นั่นไม่ได้หมายความว่าโปรแกรมเมอร์ JS สามารถเพิกเฉยต่อปัญหาทั้งหมดได้ตัวอย่างเช่นมีปัญหาการอ้างอิงแบบวงกลม JS <-> DOM ที่รู้จักกันดีใน IE รุ่นเก่าที่คุณสามารถหลีกเลี่ยงได้ในรหัส JS ของคุณ นอกจากนี้วิธีการปิดการทำงานของ JS คือคุณสมบัติการออกแบบไม่ใช่ข้อผิดพลาด แต่คุณสามารถผูกชิ้นส่วนของหน่วยความจำขนาดใหญ่กว่าที่ตั้งใจไว้หากคุณใช้การปิด "ไม่เหมาะสม" (ฉันไม่ได้บอกว่าไม่ใช้ 'em)
nnnnnn

3
การรั่วไหลของหน่วยความจำเป็นสัตว์ร้ายใน JavaScript หากคุณกำลังเขียนใบสมัคร "โครงการวิทยาลัย" ง่าย ๆ ไม่ต้องกังวล แต่เมื่อคุณเริ่มเขียนแอพระดับองค์กรประสิทธิภาพสูงการจัดการหน่วยความจำใน JavaScript เป็นสิ่งที่จำเป็น
Doug

1

บน windows คุณสามารถใช้Drip.exeเพื่อค้นหาหน่วยความจำรั่วหรือตรวจสอบว่ารูทีน mem ฟรีของคุณทำงาน

มันง่ายมากเพียงแค่ป้อน URL เว็บไซต์แล้วคุณจะเห็นปริมาณการใช้หน่วยความจำของตัวแสดงผล IE แบบรวม จากนั้นกดรีเฟรชหากหน่วยความจำเพิ่มขึ้นคุณจะพบหน่วยความจำรั่วบางแห่งบนหน้าเว็บ แต่นี่ก็มีประโยชน์มากเช่นกันเพื่อดูว่ารูทีนสำหรับการเพิ่มหน่วยความจำทำงานกับ IE ได้หรือไม่


1

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

var object = new Object();

หากคุณกำหนดตัวแปรหนึ่งให้กับตัวแปรอื่นแต่ละตัวแปรจะได้รับสำเนาของตัวชี้และทั้งคู่ยังคงอ้างอิงวัตถุเดียวกันในหน่วยความจำ

var object1 = new Object();
var object2 = object1;

ตัวแปรสองตัวชี้ไปที่วัตถุหนึ่ง

JavaScript เป็นภาษาที่รวบรวมขยะดังนั้นคุณไม่จำเป็นต้องกังวลเกี่ยวกับการจัดสรรหน่วยความจำเมื่อคุณใช้ประเภทอ้างอิง แต่ดีที่สุดที่จะdereferenceวัตถุที่คุณไม่จำเป็นต้องเพื่อให้เก็บขยะสามารถเพิ่มหน่วยความจำที่ วิธีที่ดีที่สุดในการทำเช่นนี้คือการตั้งค่าตัวแปรวัตถุให้เป็นโมฆะ

var object1 = new Object();
// do something
object1 = null; // dereference

การอ้างอิงวัตถุมีความสำคัญอย่างยิ่งในแอปพลิเคชันขนาดใหญ่มากที่ใช้วัตถุนับล้าน

จากหลักการของ JavaScript เชิงวัตถุ - NICHOLAS C. ZAKAS

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