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

Kayce Basques
Kayce Basques

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

สรุป

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

ภาพรวม

ตามเจตนารมณ์ของโมเดลประสิทธิภาพ RAIL การมุ่งเน้นด้านประสิทธิภาพควรเป็นผู้ใช้

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

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

หน่วยความจำบวม: เท่าใดจึงจะเรียกว่า "มากเกินไป"

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

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

สิ่งสำคัญคือการใช้โมเดล 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 Heap แผงตัวนับจะอยู่ใต้แผงภาพรวม ที่นี่คุณจะเห็นการใช้งานหน่วยความจำที่แยกตามฮีป JS (เหมือนกับกราฟ HEAP ในแผงภาพรวม), เอกสาร, โหนด DOM, Listener และหน่วยความจำ 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; แล้ว

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

พิมพ์ 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

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

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

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

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

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

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

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

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

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

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

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

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

หน้าผลลัพธ์ของโปรไฟล์การจัดสรร

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

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

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

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

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

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

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

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