1. กลไกการรีไซเคิลขยะ - GC
JavaScript มีกลไกการรวบรวมขยะอัตโนมัติ (GC: Garbage Collection) ซึ่งหมายความว่าสภาพแวดล้อมการดำเนินการมีหน้าที่รับผิดชอบในการจัดการหน่วยความจำที่ใช้ในระหว่างการดำเนินการรหัส
หลักการ: ตัวเก็บขยะขยะเป็นระยะ (เป็นระยะ) พบตัวแปรที่ไม่ได้ใช้งานแล้วปลดปล่อยหน่วยความจำของพวกเขา
กลไกของการรวบรวมขยะ JavaScript นั้นง่ายมาก: ค้นหาตัวแปรที่ไม่ได้ใช้อีกต่อไปแล้วจึงเพิ่มหน่วยความจำที่อยู่ อย่างไรก็ตามกระบวนการนี้ไม่ได้เป็นแบบเรียลไทม์เนื่องจากค่าใช้จ่ายค่อนข้างใหญ่ดังนั้นตัวเก็บขยะจะ ดำเนินการเป็นระยะตามช่วงเวลาที่กำหนด
ตัวแปรที่ไม่ได้ใช้อีกต่อไปคือตัวแปรที่สิ้นสุดวงจรชีวิต แน่นอนว่าพวกเขาสามารถเป็นตัวแปรท้องถิ่นเท่านั้น วงจรชีวิตของตัวแปรทั่วโลกจะไม่สิ้นสุดจนกว่าเบราว์เซอร์จะถอนการติดตั้งหน้า ตัวแปรท้องถิ่นมีอยู่เฉพาะในระหว่างการดำเนินการของฟังก์ชั่นและในกระบวนการนี้พื้นที่ที่สอดคล้องกันจะถูกจัดสรรสำหรับตัวแปรท้องถิ่นบนสแต็กหรือกองเพื่อเก็บค่าของพวกเขาและจากนั้นตัวแปรเหล่านี้จะถูกใช้ในฟังก์ชันจนกว่าจะสิ้นสุดฟังก์ชั่น อย่างไรก็ตามเนื่องจากฟังก์ชั่นภายในในการปิดฟังก์ชั่นภายนอกไม่สามารถพิจารณาได้
มาอธิบายรหัส:
ฟังก์ชั่น fn1 () {var obj = {ชื่อ: 'hanzichi', อายุ: 10};} ฟังก์ชั่น fn2 () {var obj = {ชื่อ: 'hanzichi', อายุ: 10}; return obj;} var a = fn1 (); var b = fn2 ();มาดูกันว่ารหัสถูกดำเนินการอย่างไร ก่อนอื่นมีการกำหนดฟังก์ชั่นสองฟังก์ชั่นที่เรียกว่า FN1 และ FN2 เมื่อ FN1 ถูกเรียกเข้าสู่สภาพแวดล้อม FN1 จะเปิดวัตถุหน่วยความจำ {ชื่อ: 'Hanzichi', อายุ: 10} เมื่อการโทรเสร็จสิ้นและสภาพแวดล้อม FN1 จะหมดบล็อกของหน่วยความจำจะถูกปล่อยออกมาโดยอัตโนมัติโดยตัวเก็บขยะในเครื่องยนต์ JS; ในระหว่างกระบวนการของ FN2 ที่ถูกเรียกวัตถุที่ส่งคืนจะถูกชี้ไปที่ตัวแปรส่วนกลาง B ดังนั้นบล็อกของหน่วยความจำจะไม่ถูกปล่อยออกมา
ที่นี่คำถามเกิดขึ้น: ตัวแปรใดที่ไร้ประโยชน์? ดังนั้นตัวเก็บขยะจะต้องติดตามตัวแปรที่ไร้ประโยชน์และตัวแปรเครื่องหมายที่ไม่มีประโยชน์อีกต่อไปเพื่อเรียกคืนหน่วยความจำในอนาคต กลยุทธ์สำหรับตัวแปรที่ไร้ประโยชน์ที่ใช้ในการติดแท็กอาจแตกต่างกันไปตามการใช้งานและโดยปกติจะมีสองวิธีในการทำเช่นนี้: ทำเครื่องหมายการกวาดล้างและการนับการอ้างอิง การนับจำนวนใบเสนอราคาไม่ได้เป็นเรื่องธรรมดาการล้างมาร์คมักใช้กันทั่วไป
2. เครื่องหมายชัดเจน
วิธีการรวบรวมขยะที่ใช้กันมากที่สุดใน JS คือการกำจัดเครื่องหมาย เมื่อตัวแปรเข้าสู่สภาพแวดล้อมเช่นประกาศตัวแปรในฟังก์ชันมันจะทำเครื่องหมายตัวแปรเป็น "ป้อนสภาพแวดล้อม" การพูดอย่างมีเหตุผลหน่วยความจำที่ถูกครอบครองโดยตัวแปรที่เข้าสู่สภาพแวดล้อมไม่สามารถปล่อยออกมาได้เนื่องจากอาจใช้ตราบเท่าที่กระแสการดำเนินการเข้าสู่สภาพแวดล้อมที่สอดคล้องกัน และเมื่อตัวแปรออกจากสภาพแวดล้อมมันจะถูกทำเครื่องหมายว่า "ออกจากสภาพแวดล้อม"
การทดสอบฟังก์ชั่น () {var a = 10; // ถูกทำเครื่องหมายให้ป้อนสภาพแวดล้อม var b = 20; // ถูกทำเครื่องหมายให้ป้อนการทดสอบสภาพแวดล้อม} (); // หลังจากดำเนินการแล้ว A และ B ถูกทำเครื่องหมายออกจากสภาพแวดล้อมและรีไซเคิลเมื่อตัวเก็บขยะทำงานจะทำเครื่องหมายตัวแปรทั้งหมดที่เก็บไว้ในหน่วยความจำ (แน่นอนสามารถใช้วิธีการทำเครื่องหมายใด ๆ ) จากนั้นจะลบแท็ก (ปิด) ของตัวแปรในสภาพแวดล้อมและตัวแปรที่อ้างอิงโดยตัวแปรในสภาพแวดล้อม ตัวแปรที่ทำเครื่องหมายหลังจากสิ่งนี้จะถูกพิจารณาว่าเป็นตัวแปรที่พร้อมที่จะลบเนื่องจากตัวแปรในสภาพแวดล้อมไม่สามารถเข้าถึงตัวแปรเหล่านี้ได้อีกต่อไป ในที่สุดตัวเก็บขยะก็เสร็จสิ้นงานล้างหน่วยความจำทำลายค่าที่ทำเครื่องหมายไว้และเรียกคืนพื้นที่หน่วยความจำที่พวกเขาครอบครอง
จนถึงตอนนี้การใช้งาน JS ของ IE, Firefox, Opera, Chrome และ Safari ใช้กลยุทธ์การเก็บรวบรวมขยะมาร์คหรือกลยุทธ์ที่คล้ายกัน แต่ช่วงเวลาของการรวบรวมขยะนั้นแตกต่างกัน
3. จำนวนใบเสนอราคา
ความหมายของจำนวนการอ้างอิงคือการติดตามจำนวนครั้งที่แต่ละค่ามีการอ้างอิง เมื่อมีการประกาศตัวแปรและค่าประเภทการอ้างอิงถูกกำหนดให้กับตัวแปรจำนวนการอ้างอิงถึงค่านี้คือ 1 ถ้าค่าเดียวกันถูกกำหนดให้กับตัวแปรอื่นจำนวนการอ้างอิงถึงค่าจะเพิ่มขึ้น 1 ในทางตรงกันข้าม พื้นที่ที่มันครอบครองสามารถเรียกคืนได้ ด้วยวิธีนี้เมื่อนักสะสมขยะทำงานอีกครั้งในครั้งต่อไปมันจะช่วยเพิ่มความจำที่มีค่าด้วยการอ้างอิง 0
การทดสอบฟังก์ชั่น () {var a = {}; // จำนวนการอ้างอิงของ A คือ 0 var b = a; // จำนวนการอ้างอิงของ A คือ 1 var c = a; // จำนวนการอ้างอิงของ A คือ 1 และจำนวนการอ้างอิงของ A คือ 1 และจำนวนการอ้างอิงของ A คือ 1 และจำนวนการอ้างอิงของ A คือ 2 var B = {}; // จำนวนการอ้างอิงของ A คือ 1 และจำนวนการอ้างอิงของ A คือ 1}NetScape Navigator3 เป็นเบราว์เซอร์แรกที่ใช้กลยุทธ์การนับอ้างอิง แต่ในไม่ช้ามันก็พบปัญหาร้ายแรง: การอ้างอิงแบบวงกลม การอ้างอิงแบบวงกลมหมายถึงวัตถุ A ที่มีตัวชี้ไปยังวัตถุ B และวัตถุ B ยังมีการอ้างอิงถึงวัตถุ A.
ฟังก์ชั่น fn () {var a = {}; var b = {}; A.pro = B; b.pro = a;} fn ();เวลาอ้างอิงของ A และ B ด้านบนคือ 2 หลังจากดำเนินการ fn () วัตถุทั้งสองได้ออกจากสภาพแวดล้อม ไม่มีปัญหาในโหมดการล้างเครื่องหมาย อย่างไรก็ตามภายใต้กลยุทธ์การนับอ้างอิงเนื่องจากเวลาอ้างอิงของ A และ B ไม่ใช่ 0 หน่วยความจำจะไม่ถูกรวบรวมโดยนักสะสมขยะ หากฟังก์ชั่น FN ถูกเรียกในปริมาณมากการรั่วไหลของหน่วยความจำจะเกิดขึ้น ใน IE7 และ IE8 หน่วยความจำเพิ่มขึ้นอย่างรวดเร็ว
เรารู้ว่าวัตถุบางอย่างใน IE ไม่ใช่วัตถุ JS ดั้งเดิม ตัวอย่างเช่นวัตถุใน DOM และ BOM ถูกนำไปใช้ในรูปแบบของวัตถุ COM โดยใช้ C ++ และกลไกการรวบรวมขยะของวัตถุ COM ใช้กลยุทธ์การนับอ้างอิง ดังนั้นแม้ว่าเอ็นจิ้น IE JS จะใช้กลยุทธ์การล้างแท็ก แต่วัตถุ COM ที่เข้าถึงโดย JS ยังคงอยู่บนพื้นฐานของกลยุทธ์การนับอ้างอิง กล่าวอีกนัยหนึ่งตราบใดที่วัตถุ COM มีส่วนร่วมใน IE จะมีปัญหาของการอ้างอิงแบบวงกลม
var element = document.getElementById ("some_element"); var myObject = new Object (); myObject.e = element; element.o = myObject;ตัวอย่างนี้สร้างการอ้างอิงแบบวงกลมระหว่างองค์ประกอบ DOM และวัตถุ JS ดั้งเดิม ในหมู่พวกเขาตัวแปร myObject มีแอตทริบิวต์ชื่อองค์ประกอบชี้ไปที่วัตถุองค์ประกอบ และองค์ประกอบตัวแปรยังมีแอตทริบิวต์ชื่อ O กลับไปที่อ้างถึง MyObject เนื่องจากการอ้างอิงแบบวงกลมนี้แม้ว่า DOM ในตัวอย่างจะถูกลบออกจากหน้าเว็บมันจะไม่ถูกนำกลับมาใช้ใหม่
เมื่อดูตัวอย่างข้างต้นนักเรียนบางคนคิดว่ามันอ่อนแอเกินไป ใครจะทำสิ่งที่น่าเบื่อเช่นนี้? อันที่จริงเรากำลังทำอยู่หรือไม่?
window.onload = function outerfunction () {var obj = document.getElementById ("องค์ประกอบ"); obj.onclick = ฟังก์ชั่น innerfunction () {};};รหัสนี้ดูเหมือนจะไม่เป็นไร แต่ OBJ หมายถึง document.getElementById ("องค์ประกอบ") และวิธี onclick ของ document.getElementById ("องค์ประกอบ") จะอ้างถึงตัวแปรเยอรมันในสภาพแวดล้อมภายนอกซึ่งตามธรรมชาติยังรวมถึง OBJ มันซ่อนอยู่มากเหรอ?
สารละลาย
วิธีที่ง่ายที่สุดคือการไม่อ้างอิงลูปด้วยตนเองเช่นฟังก์ชั่นตอนนี้สามารถทำได้
myObject.element = null; element.o = null;
window.onload = function outerfunction () {var obj = document.getElementById ("องค์ประกอบ"); obj.onclick = function innerfunction () {}; obj = null;};การตั้งค่าตัวแปรเป็นโมฆะหมายถึงการตัดการเชื่อมต่อระหว่างตัวแปรและค่าที่อ้างอิงก่อนหน้านี้ เมื่อตัวเก็บขยะในครั้งต่อไปค่าเหล่านี้จะถูกลบและหน่วยความจำที่พวกเขาครอบครองจะถูกนำกลับมาใช้ใหม่
ควรสังเกตว่า IE9+ ไม่มีการอ้างอิงแบบวงกลมเพื่อทำให้เกิดการรั่วไหลของหน่วยความจำ DOM อาจเป็นได้ว่า Microsoft ได้ปรับให้เหมาะสมหรือวิธีการรีไซเคิลของ DOM เปลี่ยนไป
4. การจัดการหน่วยความจำ
1. การรวบรวมขยะจะถูกกระตุ้นเมื่อใด
นักสะสมขยะทำงานเป็นระยะ หากหน่วยความจำที่จัดสรรมีขนาดใหญ่มากงานรีไซเคิลจะยากมาก การกำหนดช่วงเวลาการรวบรวมขยะกลายเป็นคำถามที่ควรค่าแก่การคิด คอลเลกชันขยะของ IE6 ทำงานตามการจัดสรรหน่วยความจำ เมื่อมีตัวแปร 256 ตัววัตถุ 4096 และสตริง 64K ในสภาพแวดล้อมตัวเก็บขยะจะถูกกระตุ้น มันดูมีวิทยาศาสตร์มากและไม่จำเป็นต้องถูกเรียกอีกครั้งในขณะที่ บางครั้งมันก็ไม่จำเป็น การโทรตามความต้องการแบบนี้ไม่ดีเหรอ? แต่ถ้ามีตัวแปรมากมายในสภาพแวดล้อมและสคริปต์มีความซับซ้อนและเป็นปกติในตอนนี้ผลลัพธ์ก็คือตัวเก็บขยะนั้นใช้งานได้เสมอดังนั้นเบราว์เซอร์จึงไม่สามารถเล่นได้
Microsoft ได้ทำการปรับเปลี่ยนใน IE7 เงื่อนไขทริกเกอร์ไม่ได้รับการแก้ไขอีกต่อไป แต่ได้รับการแก้ไขแบบไดนามิก ค่าเริ่มต้นเหมือนกับ IE6 หากการจัดสรรหน่วยความจำที่รวบรวมโดยตัวเก็บขยะนั้นน้อยกว่า 15% ของหน่วยความจำที่ถูกครอบครองโดยโปรแกรมหมายความว่าหน่วยความจำส่วนใหญ่ไม่สามารถรีไซเคิลได้ เงื่อนไขทริกเกอร์คอลเลกชันขยะมีความอ่อนไหวเกินไป ในเวลานี้เงื่อนไขถนนเป็นสองเท่า หากหน่วยความจำที่รวบรวมสูงกว่า 85%หมายความว่าหน่วยความจำส่วนใหญ่ควรทำความสะอาดมานานแล้ว ในเวลานี้เงื่อนไขทริกเกอร์ถูกตั้งค่ากลับ สิ่งนี้ทำให้การรีไซเคิลขยะทำงานได้ดีขึ้นฟังก์ชั่น
2. โซลูชัน GC ที่สมเหตุสมผล
1) โซลูชัน GC พื้นฐานของเครื่องยนต์ JavaScript คือ (Simple GC): Mark and Sweep, I.e.:
2) ข้อบกพร่องของ GC
เช่นเดียวกับภาษาอื่น ๆ นโยบาย GC ของ JavaScript ไม่สามารถหลีกเลี่ยงปัญหาหนึ่ง: เมื่อ GC หยุดตอบสนองต่อการดำเนินการอื่น ๆ ซึ่งเป็นเหตุผลด้านความปลอดภัย GC ของ JavaScript คือ 100ms หรือเหนือกว่าซึ่งเป็นสิ่งที่ดีสำหรับแอปพลิเคชันทั่วไป แต่สำหรับเกม JS แอปพลิเคชันที่ต้องการการเชื่อมโยงกันสูงนั้นลำบาก นี่คือสิ่งที่เอ็นจิ้นใหม่จำเป็นต้องปรับให้เหมาะสม: หลีกเลี่ยงการตอบสนองการหยุดระยะยาวที่เกิดจาก GC
3) กลยุทธ์การเพิ่มประสิทธิภาพ GC
ลุงเดวิดส่วนใหญ่แนะนำโซลูชันการเพิ่มประสิทธิภาพสองวิธีและสิ่งเหล่านี้ยังเป็นโซลูชั่นการเพิ่มประสิทธิภาพสองประการที่สำคัญที่สุด:
(1) Generation GC)
สิ่งนี้สอดคล้องกับแนวคิดกลยุทธ์การรีไซเคิล Java วัตถุประสงค์คือเพื่อแยกความแตกต่างระหว่างวัตถุ "ชั่วคราว" และ "ถาวร"; รีไซเคิลพื้นที่ "วัตถุชั่วคราว" มากขึ้นและพื้นที่ที่มี "วัตถุ" น้อยลง "ลดวัตถุที่จำเป็นต้องผ่านการสำรวจในแต่ละครั้งซึ่งจะช่วยลดเวลาที่ใช้ใน GCS ในแต่ละครั้ง ดังที่แสดงในภาพ:
สิ่งที่ต้องเพิ่มที่นี่คือสำหรับวัตถุรุ่นที่ดำรงตำแหน่งมีค่าใช้จ่ายเพิ่มเติม: โยกย้ายจากคนรุ่นใหม่ไปยังรุ่นที่ดำรงตำแหน่งและหากมีการอ้างอิงการชี้อ้างอิงจะต้องมีการแก้ไข
(2) GC ที่เพิ่มขึ้น
ความคิดของแผนนี้ง่ายมากซึ่งก็คือ "ปฏิบัติต่อเล็กน้อยในแต่ละครั้งจัดการกับในครั้งต่อไปในครั้งต่อไปและอื่น ๆ " ดังที่แสดงในภาพ:
แม้ว่าโซลูชันนี้ใช้เวลาสั้น ๆ แต่ก็มีการหยุดชะงักหลายครั้งซึ่งทำให้เกิดปัญหาการสลับบริบทบ่อยครั้ง
เนื่องจากโซลูชันแต่ละครั้งมีสถานการณ์และข้อเสียที่เกี่ยวข้องในแอปพลิเคชันจริงโซลูชันจะถูกเลือกตามสถานการณ์จริง
ตัวอย่างเช่น: เมื่ออัตราส่วน (วัตถุ/s) ต่ำความถี่ของการดำเนินการของ GC จะถูกขัดจังหวะและ GC อย่างง่ายจะต่ำกว่า; หากวัตถุจำนวนมาก "รอดชีวิต" เป็นเวลานานข้อได้เปรียบของการประมวลผลทั่วไปนั้นไม่ค่อยดีนัก
บทความข้างต้นเข้าใจอย่างครอบคลุมถึงกลไกการรีไซเคิลการรวบรวมขยะของ Javascirp เป็นเนื้อหาทั้งหมดที่ฉันแบ่งปันกับคุณ ฉันหวังว่ามันจะให้ข้อมูลอ้างอิงและฉันหวังว่าคุณจะสนับสนุน wulin.com มากขึ้น