แก้ปัญหาเกี่ยวกับหน่วยความจำ

ดูวิธีใช้ Chrome และ DevTools เพื่อค้นหาปัญหาเกี่ยวกับหน่วยความจำที่ส่งผลต่อประสิทธิภาพของหน้าเว็บ ซึ่งรวมถึงการรั่วไหลของหน่วยความจำ หน่วยความจําที่เพิ่มขึ้น และการเก็บขยะบ่อยครั้ง

สรุป

  • ดูว่าหน้าเว็บใช้หน่วยความจำเท่าใดด้วยตัวจัดการงานของ Chrome
  • แสดงภาพการใช้งานหน่วยความจำเมื่อเวลาผ่านไปด้วยการบันทึกไทม์ไลน์
  • ระบุแผนผัง DOM ที่แยกออก (สาเหตุที่พบบ่อยของการรั่วไหลของหน่วยความจํา) ด้วยภาพรวมกอง heap
  • ดูว่ามีการจองหน่วยความจำใหม่ในกอง JS เมื่อใดด้วยการบันทึกไทม์ไลน์การจัดสรร
  • ระบุองค์ประกอบที่แยกออกซึ่งเก็บไว้โดยการอ้างอิง JavaScript

ภาพรวม

หัวใจสําคัญของรูปแบบประสิทธิภาพ RAIL คือผู้ใช้ควรเป็นจุดสนใจของการพัฒนาประสิทธิภาพ

ปัญหาเกี่ยวกับหน่วยความจํามีความสําคัญเนื่องจากผู้ใช้มักจะรับรู้ถึงปัญหาเหล่านี้ ผู้ใช้จะรับรู้ปัญหาเกี่ยวกับความทรงจำได้ดังนี้

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

หน่วยความจําที่เพิ่มขึ้น: "มากเกินไป" หมายถึงเท่าใด

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

เราไม่สามารถใช้ตัวเลขที่แน่นอนได้ เนื่องจากอุปกรณ์และเบราว์เซอร์แต่ละประเภทมีความสามารถแตกต่างกัน หน้าเดียวกันที่ทํางานได้อย่างราบรื่นในสมาร์ทโฟนระดับสูงอาจขัดข้องในสมาร์ทโฟนระดับล่าง

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

ตรวจสอบการใช้หน่วยความจำแบบเรียลไทม์ด้วยตัวจัดการงานของ Chrome

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

  1. กด Shift+Esc หรือไปที่เมนูหลักของ Chrome แล้วเลือกเครื่องมือเพิ่มเติม > ตัวจัดการงานเพื่อเปิดตัวจัดการงาน

    การเปิดตัวจัดการงาน

  2. คลิกขวาที่ส่วนหัวของตารางในตัวจัดการงาน แล้วเปิดใช้หน่วยความจำ JavaScript

    การเปิดใช้หน่วยความจํา JS ในส่วนหัวของตัวจัดการงาน

คอลัมน์ 2 คอลัมน์นี้บอกข้อมูลที่แตกต่างกันเกี่ยวกับวิธีที่หน้าเว็บใช้หน่วยความจํา

  • คอลัมน์หน่วยความจําที่ใช้แสดงหน่วยความจําของระบบปฏิบัติการ โหนด DOM จะจัดเก็บไว้ในหน่วยความจําของระบบปฏิบัติการ หากค่านี้เพิ่มขึ้น ระบบจะสร้างโหนด DOM
  • คอลัมน์หน่วยความจํา JavaScript แสดงกอง JS คอลัมน์นี้มี 2 ค่า ค่าที่คุณสนใจคือตัวเลขจริง (ตัวเลขในวงเล็บ) ตัวเลขที่แสดงอยู่แสดงถึงจํานวนหน่วยความจําที่ออบเจ็กต์ที่เข้าถึงได้ในหน้าเว็บใช้อยู่ หากตัวเลขนี้เพิ่มขึ้น แสดงว่าระบบกําลังสร้างออบเจ็กต์ใหม่หรือออบเจ็กต์ที่มีอยู่มีขนาดใหญ่ขึ้น

    ตัวจัดการงานที่เปิดใช้ส่วนหัวหน่วยความจําของ JavaScript

แสดงภาพหน่วยความจําที่รั่วด้วยไฟล์บันทึกประสิทธิภาพ

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

  1. เปิดแผงประสิทธิภาพในเครื่องมือสำหรับนักพัฒนาเว็บ
  2. เปิดใช้ช่องทำเครื่องหมายความทรงจำ
  3. บันทึกเสียง

ลองดูโค้ดต่อไปนี้เพื่อสาธิตการบันทึกหน่วยความจําของประสิทธิภาพ

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

ทุกครั้งที่กดปุ่มที่อ้างอิงในโค้ด ระบบจะเพิ่มโหนด div 10,000 รายการต่อท้ายเนื้อหาเอกสาร และพุชสตริงอักขระ x 1 ล้านรายการไปยังอาร์เรย์ x การรันโค้ดนี้จะสร้างการบันทึกไทม์ไลน์ดังภาพหน้าจอต่อไปนี้

ตัวอย่างการเติบโตแบบง่าย

ขั้นแรกคือคําอธิบายอินเทอร์เฟซผู้ใช้ กราฟ HEAP ในแผงภาพรวม (ใต้ NET) แสดงกอง JS แผงตัวนับจะอยู่ใต้แผงภาพรวม ในส่วนนี้ คุณสามารถดูรายละเอียดการใช้งานหน่วยความจําตามฮีป JS (เหมือนกับกราฟ HEAP ในแผงภาพรวม) เอกสาร โหนด DOM รายการฟัง และหน่วยความจํา GPU การปิดใช้ช่องทําเครื่องหมายจะซ่อนช่องนั้นจากกราฟ

ตอนนี้มาดูการวิเคราะห์โค้ดเทียบกับภาพหน้าจอ หากดูตัวนับโหนด (กราฟสีเขียว) คุณจะเห็นว่ามันตรงกับโค้ดอย่างสมบูรณ์ จำนวนโหนดจะเพิ่มขึ้นทีละขั้น คุณสามารถสันนิษฐานได้ว่าการเพิ่มขึ้นของจำนวนโหนดแต่ละครั้งคือการเรียกใช้ grow() กราฟกองขยะ JS (กราฟสีน้ำเงิน) นั้นไม่ตรงไปตรงมา ตามแนวทางปฏิบัติแนะนำ การลดลงครั้งแรกนั้นคือการรวบรวมขยะแบบบังคับ (ทำได้โดยกดปุ่มรวบรวมขยะ) เมื่อการบันทึกดำเนินไปเรื่อยๆ คุณจะเห็นขนาดฮีป JS เพิ่มขึ้น กรณีนี้เป็นเรื่องปกติและเป็นสิ่งที่คาดไว้ เนื่องจากโค้ด JavaScript กำลังสร้างโหนด DOM เมื่อมีการคลิกปุ่มทุกครั้ง และทํางานจํานวนมากเมื่อสร้างสตริงที่มีอักขระ 1 ล้านตัว ประเด็นสำคัญคือกอง JS สิ้นสุดสูงกว่าตอนเริ่มต้น (ซึ่ง "ตอนเริ่มต้น" ในที่นี้คือจุดหลังจากการบังคับใช้การเก็บขยะ) ในชีวิตจริง หากเห็นรูปแบบการเพิ่มขนาดฮีป JS หรือขนาดโหนด แสดงว่าอาจมีการสูญเสียหน่วยความจํา

ค้นหาหน่วยความจําที่รั่วไหลของแผนผัง DOM ที่แยกออกด้วยสแนปชอตฮีป

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

ต่อไปนี้เป็นตัวอย่างง่ายๆ ของโหนด DOM ที่แยกออก

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

การคลิกปุ่มที่อ้างอิงในโค้ดจะสร้างโหนด ul ที่มีโหนดย่อย li 10 โหนด โค้ดอ้างอิงโหนดเหล่านี้ แต่โหนดดังกล่าวไม่อยู่ในต้นไม้ DOM จึงถูกแยกออก

สแนปชอตฮีปเป็นวิธีหนึ่งในการระบุโหนดที่แยกออก สแนปชอตฮีปจะแสดงการแจกแจงหน่วยความจําของออบเจ็กต์ JS และโหนด DOM ของหน้า ณ เวลาที่สแนปชอต

หากต้องการสร้างสแนปชอต ให้เปิดเครื่องมือสำหรับนักพัฒนาซอฟต์แวร์และไปที่แผงหน่วยความจำ เลือกปุ่มตัวเลือกสแนปชอตกอง แล้วกดปุ่มจับภาพสแนปชอต

เลือกปุ่มตัวเลือก &quot;ถ่ายสแนปชอตฮีป&quot; แล้ว

ระบบอาจใช้เวลาสักครู่ในการประมวลผลและโหลดภาพรวม เมื่อดำเนินการเสร็จแล้ว ให้เลือกจากแผงด้านซ้าย (ชื่อ Heap snapshots)

พิมพ์ Detached ในช่องป้อนข้อมูลตัวกรองคลาสเพื่อค้นหาต้นไม้ DOM ที่แยกออก

การกรองหาโหนดที่แยกออก

ขยายเครื่องหมายกะรัตเพื่อตรวจสอบต้นไม้ที่แยกออกมา

การตรวจสอบต้นไม้ที่แยกออกมา

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

กำลังตรวจสอบโหนดที่แยกออก

ระบุหน่วยความจําฮีปของ JS ที่รั่วไหลด้วยไทม์ไลน์การจัดสรร

ไทม์ไลน์การจัดสรรเป็นเครื่องมืออีกอย่างที่จะช่วยคุณติดตามการสูญเสียหน่วยความจําในกอง JS

ตัวอย่างโค้ดต่อไปนี้แสดงไทม์ไลน์การจัดสรร

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

ทุกครั้งที่กดปุ่มที่อ้างอิงในโค้ด ระบบจะเพิ่มสตริงที่มีอักขระ 1 ล้านตัวลงในอาร์เรย์ x

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

ขณะบันทึก ให้สังเกตว่าแถบสีน้ำเงินปรากฏในไทม์ไลน์การจัดสรรหรือไม่ ดังในภาพหน้าจอต่อไปนี้

การจัดสรรใหม่ในไทม์ไลน์ประสิทธิภาพ

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

ไทม์ไลน์การจัดสรรที่ซูม

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

รายละเอียดออบเจ็กต์ของอาร์เรย์สตริง

ตรวจสอบการจัดสรรหน่วยความจําตามฟังก์ชัน

ใช้โปรไฟล์ประเภทการสุ่มตัวอย่างการจัดสรรในแผงหน่วยความจําเพื่อดูการจัดสรรหน่วยความจําตามฟังก์ชัน JavaScript

เครื่องมือสร้างโปรไฟล์การสุ่มตัวอย่างการจัดสรรในแผงหน่วยความจำ

  1. เลือกปุ่มตัวเลือกการสุ่มตัวอย่างการกําหนดราคา หากมีเวิร์กเกอร์ในหน้าเว็บ คุณจะเลือกเวิร์กเกอร์นั้นให้เป็นเป้าหมายการสร้างโปรไฟล์ได้จากหน้าต่างเลือกอินสแตนซ์ VM ของ JavaScript
  2. กดปุ่มเริ่ม
  3. ดําเนินการในหน้าที่ต้องการตรวจสอบ
  4. กดปุ่มหยุดเมื่อดำเนินการเสร็จแล้ว

เครื่องมือสำหรับนักพัฒนาซอฟต์แวร์จะแสดงรายละเอียดการจัดสรรหน่วยความจําตามฟังก์ชัน มุมมองเริ่มต้นคือมาก (จากล่างขึ้นบน) ซึ่งจะแสดงฟังก์ชันที่จัดสรรหน่วยความจำมากที่สุดที่ด้านบน

หน้าผลการค้นหาโปรไฟล์การจัดสรร

ระบุออบเจ็กต์ที่เก็บไว้โดยการอ้างอิง JS

โปรไฟล์องค์ประกอบที่แยกออกจะแสดงองค์ประกอบที่แยกออกซึ่งยังคงอยู่เนื่องจากโค้ด JavaScript อ้างอิงถึง

บันทึกโปรไฟล์องค์ประกอบที่แยกออกเพื่อดูโหนด HTML และจํานวนโหนดที่แน่นอน

ตัวอย่างโปรไฟล์องค์ประกอบที่แยกออก

สังเกตจุดที่เก็บขยะบ่อยครั้ง

หากหน้าเว็บหยุดชั่วคราวบ่อยครั้ง คุณอาจมีปัญหาเกี่ยวกับการเก็บขยะ

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

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