ดูวิธีบันทึกสแนปชอตฮีปด้วยหน่วยความจํา > โปรไฟล์ > สแนปชอตฮีป และค้นหาการรั่วไหลของหน่วยความจํา
เครื่องมือวิเคราะห์ฮีปแสดงการแจกแจงหน่วยความจําตามออบเจ็กต์ JavaScript และโหนด DOM ที่เกี่ยวข้องในหน้า ใช้เพื่อจับภาพฮีป JS, วิเคราะห์กราฟหน่วยความจํา, เปรียบเทียบภาพรวม และค้นหาหน่วยความจําที่รั่วไหล โปรดดูข้อมูลเพิ่มเติมที่หัวข้อต้นไม้การเก็บรักษาออบเจ็กต์
ถ่ายสแนปชอต
วิธีถ่ายภาพสแนปชอตฮีป
- ในหน้าที่ต้องการสร้างโปรไฟล์ ให้เปิดเครื่องมือสำหรับนักพัฒนาเว็บ แล้วไปที่แผงหน่วยความจำ
- เลือกประเภทการโปรไฟล์ ฮีปสแนปชอต จากนั้นเลือกอินสแตนซ์ VM ของ JavaScript แล้วคลิกจับภาพ
เมื่อแผงหน่วยความจําโหลดและแยกวิเคราะห์สแนปชอตแล้ว ก็จะแสดงขนาดรวมของออบเจ็กต์ JavaScript ที่เข้าถึงได้ใต้ชื่อสแนปชอตในส่วนสแนปชอตฮีป
ภาพรวมจะแสดงเฉพาะออบเจ็กต์จากกราฟหน่วยความจำที่เข้าถึงได้จากออบเจ็กต์ส่วนกลาง การเก็บสแนปชอตจะเริ่มต้นด้วยการรวบรวมขยะเสมอ
จํานวนหนึ่งล้างสแนปชอต
หากต้องการนำภาพหน้าจอทั้งหมดออก ให้คลิก
ล้างโปรไฟล์ทั้งหมดดูสแนปชอต
หากต้องการตรวจสอบภาพรวมจากมุมมองต่างๆ เพื่อวัตถุประสงค์ที่แตกต่างกัน ให้เลือกมุมมองใดมุมมองหนึ่งจากเมนูแบบเลื่อนลงที่ด้านบน
ดู | เนื้อหา | วัตถุประสงค์ |
---|---|---|
สรุป | ออบเจ็กต์ที่จัดกลุ่มตามชื่อคอนสตรัคเตอร์ | ใช้เพื่อค้นหาออบเจ็กต์และการใช้หน่วยความจําของออบเจ็กต์ตามประเภท มีประโยชน์สําหรับการติดตามการรั่วไหลของ DOM |
การเปรียบเทียบ | ความแตกต่างระหว่างภาพรวม 2 รายการ | ใช้เพื่อเปรียบเทียบภาพรวม 2 ภาพ (หรือมากกว่า) ก่อนและหลังการดำเนินการ ยืนยันการมีอยู่และสาเหตุของการรั่วไหลของหน่วยความจําโดยตรวจสอบค่าต่างของหน่วยความจําที่เพิ่มและจํานวนการอ้างอิง |
การจำกัด | เนื้อหาฮีป | ให้มุมมองที่ดีขึ้นของโครงสร้างออบเจ็กต์ และช่วยวิเคราะห์ออบเจ็กต์ที่อ้างอิงในเนมสเปซส่วนกลาง (หน้าต่าง) เพื่อค้นหาสิ่งที่ทำให้ออบเจ็กต์เหล่านั้นยังคงอยู่ ใช้เพื่อวิเคราะห์การปิดและเจาะลึกวัตถุในระดับต่ำ |
สถิติ | แผนภูมิวงกลมของการจัดสรรหน่วยความจํา | ดูขนาดสัมพัทธ์ของหน่วยความจําบางส่วนที่จัดสรรให้กับโค้ด สตริง อาร์เรย์ JS อาร์เรย์ที่มีการจัดประเภท และออบเจ็กต์ของระบบ |
มุมมองสรุป
ในตอนแรก สแนปชอตกองขยะจะเปิดขึ้นในมุมมองสรุปซึ่งแสดงตัวสร้างในคอลัมน์ คุณสามารถขยายตัวสร้างคอนสตรัคเตอร์เพื่อดูออบเจ็กต์ที่สร้างอินสแตนซ์ได้
หากต้องการกรองคอนสตรัคเตอร์ที่ไม่เกี่ยวข้องออก ให้พิมพ์ชื่อที่ต้องการตรวจสอบในตัวกรองคลาสที่ด้านบนของมุมมองสรุป
ตัวเลขข้างชื่อคอนสตรัคเตอร์แสดงจํานวนออบเจ็กต์ทั้งหมดที่สร้างด้วยคอนสตรัคเตอร์ มุมมองสรุปจะแสดงคอลัมน์ต่อไปนี้ด้วย
- ระยะทางแสดงระยะทางไปยังรูทโดยใช้เส้นทางแบบง่ายที่สั้นที่สุดของโหนด
- ขนาดแบบตื้นแสดงผลรวมของขนาดแบบตื้นของออบเจ็กต์ทั้งหมดที่สร้างโดยตัวสร้างบางรายการ ขนาดแบบตื้นคือขนาดหน่วยความจําที่ออบเจ็กต์หนึ่งๆ ใช้ โดยทั่วไปแล้ว อาร์เรย์และสตริงจะมีขนาดแบบตื้นที่ใหญ่กว่า ดูขนาดของวัตถุด้วย
- ขนาดที่เก็บไว้แสดงขนาดสูงสุดที่เก็บไว้ในกลุ่มออบเจ็กต์เดียวกัน ขนาดที่เก็บไว้คือขนาดของหน่วยความจำที่คุณเพิ่มได้โดยการลบออบเจ็กต์และทำให้ออบเจ็กต์ที่ขึ้นต่อกันเข้าถึงไม่ได้อีกต่อไป ดูขนาดของวัตถุด้วย
เมื่อคุณขยายตัวสร้าง มุมมองสรุปจะแสดงอินสแตนซ์ทั้งหมดของตัวสร้าง แต่ละอินสแตนซ์จะได้รับรายละเอียดขนาดแบบตื้นและแบบเก็บรักษาไว้ในคอลัมน์ที่เกี่ยวข้อง ตัวเลขหลังอักขระ @
คือรหัสที่ไม่ซ้ำของออบเจ็กต์ ซึ่งช่วยให้คุณเปรียบเทียบสแนปชอตกองขยะตามแต่ละออบเจ็กต์ได้
ตัวกรองคอนสตรัคเตอร์
มุมมองสรุปช่วยให้คุณกรองคอนสตรัคเตอร์ตามกรณีทั่วไปของการใช้หน่วยความจำที่ไม่มีประสิทธิภาพ
หากต้องการใช้ตัวกรองเหล่านี้ ให้เลือกตัวเลือกใดตัวเลือกหนึ่งต่อไปนี้จากเมนูแบบเลื่อนลงด้านขวาสุดในแถบการดำเนินการ
- วัตถุทั้งหมด: วัตถุทั้งหมดที่บันทึกโดยภาพนิ่งปัจจุบัน ตั้งค่าไว้โดยค่าเริ่มต้น
- ออบเจ็กต์ที่จัดสรรก่อนสแนปชอต 1: ออบเจ็กต์ที่สร้างและยังคงอยู่ในหน่วยความจำก่อนที่จะมีการจับภาพสแนปชอตแรก
- ออบเจ็กต์ที่จัดสรรระหว่างภาพรวม 1 กับภาพรวม 2: ดูความแตกต่างของออบเจ็กต์ระหว่างภาพรวมล่าสุดกับภาพรวมก่อนหน้า ภาพรวมใหม่แต่ละรายการจะเพิ่มตัวกรองนี้ลงในรายการแบบเลื่อนลง
- สตริงที่ซ้ำกัน: ค่าสตริงที่เก็บไว้ในหน่วยความจําหลายครั้ง
- ออบเจ็กต์ที่เก็บไว้โดยโหนดที่แยกออก: ออบเจ็กต์ที่ยังคงอยู่เนื่องจากโหนด DOM ที่แยกออกอ้างอิงถึง
- ออบเจ็กต์ที่เก็บไว้โดยคอนโซลเครื่องมือสำหรับนักพัฒนาเว็บ: ออบเจ็กต์ที่เก็บไว้ในหน่วยความจําเนื่องจากมีการประเมินหรือโต้ตอบผ่านคอนโซลเครื่องมือสําหรับนักพัฒนาเว็บ
รายการพิเศษในสรุป
นอกจากการจัดกลุ่มตามคอนสตรคเตอร์แล้ว มุมมองสรุปยังจัดกลุ่มออบเจ็กต์ตามข้อมูลต่อไปนี้ด้วย
- ฟังก์ชันในตัว เช่น
Array
หรือObject
- เอลิเมนต์ HTML ที่แบ่งกลุ่มตามแท็ก เช่น
<div>
,<a>
,<img>
และอื่นๆ - ฟังก์ชันที่คุณกำหนดไว้ในโค้ด
- หมวดหมู่พิเศษที่ไม่ได้อิงตามตัวสร้าง
(array)
หมวดหมู่นี้ประกอบด้วยออบเจ็กต์ที่คล้ายอาร์เรย์ภายในต่างๆ ที่ไม่ตรงกับออบเจ็กต์ที่มองเห็นได้ใน JavaScript โดยตรง
ตัวอย่างเช่น เนื้อหาของออบเจ็กต์ Array
ของ JavaScript จะจัดเก็บไว้ในออบเจ็กต์ภายในรองชื่อ (object elements)[]
เพื่อให้ปรับขนาดได้ง่ายขึ้น ในทํานองเดียวกัน พร็อพเพอร์ตี้ที่มีชื่อในออบเจ็กต์ JavaScript มักจะจัดเก็บไว้ในออบเจ็กต์ภายในรองชื่อ (object properties)[]
ซึ่งแสดงอยู่ในหมวดหมู่ (array)
ด้วย
(compiled code)
หมวดหมู่นี้ประกอบด้วยข้อมูลภายในที่ V8 ต้องการเพื่อเรียกใช้ฟังก์ชันที่ JavaScript หรือ WebAssembly กำหนด ฟังก์ชันแต่ละรายการสามารถแสดงได้หลายวิธี ตั้งแต่ขนาดเล็กและช้าไปจนถึงขนาดใหญ่และเร็ว
V8 จะจัดการการใช้หน่วยความจําในหมวดหมู่นี้โดยอัตโนมัติ หากฟังก์ชันทำงานหลายครั้ง V8 จะใช้หน่วยความจํามากขึ้นสําหรับฟังก์ชันนั้นเพื่อให้ทํางานได้เร็วขึ้น หากฟังก์ชันใดไม่ได้ทำงานมาเป็นเวลานาน V8 อาจล้างข้อมูลภายในของฟังก์ชันนั้น
(concatenated string)
เมื่อ V8 รวมสตริง 2 รายการเข้าด้วยกัน เช่น โดยใช้โอเปอเรเตอร์ +
ของ JavaScript ระบบอาจเลือกแสดงผลลัพธ์ภายในเป็น "สตริงที่ต่อเชื่อมกัน" หรือที่เรียกว่าโครงสร้างข้อมูล Rope
V8 จะจัดสรรออบเจ็กต์ขนาดเล็กที่มีช่องภายในชื่อ first
และ second
ซึ่งชี้ไปยังสตริงต้นทาง 2 รายการแทนการคัดลอกอักขระทั้งหมดของสตริงต้นทาง 2 รายการลงในสตริงใหม่ ซึ่งจะช่วยให้ V8 ประหยัดเวลาและหน่วยความจำ จากมุมมองของโค้ด JavaScript สตริงเหล่านี้เป็นเพียงสตริงปกติและทํางานเหมือนสตริงอื่นๆ
InternalNode
หมวดหมู่นี้แสดงออบเจ็กต์ที่จัดสรรนอก V8 เช่น ออบเจ็กต์ C++ ที่ Blink กำหนด
หากต้องการดูชื่อคลาส C++ ให้ใช้ Chrome สําหรับการทดสอบ แล้วทําดังนี้
- เปิดเครื่องมือสำหรับนักพัฒนาเว็บ แล้วเปิด การตั้งค่า > การทดสอบ > แสดงตัวเลือกเพื่อแสดงข้อมูลภายในในสแนปชอตกอง
- เปิดแผงหน่วยความจํา เลือก สแนปชอตกอง และเปิด เปิดเผยข้อมูลภายใน (รวมถึงรายละเอียดเพิ่มเติมเฉพาะสำหรับการใช้งาน)
- จำลองปัญหาที่ทำให้
InternalNode
ใช้หน่วยความจำเป็นจำนวนมาก - ถ่ายสแนปชอตฮีป ในภาพหน้าจอนี้ ออบเจ็กต์จะมีชื่อคลาส C++ แทน
InternalNode
(object shape)
ตามที่อธิบายไว้ในพร็อพเพอร์ตี้ที่รวดเร็วใน V8 นั้น V8 จะติดตามคลาสที่ซ่อนอยู่ (หรือรูปร่าง) เพื่อให้แสดงออบเจ็กต์หลายรายการที่มีพร็อพเพอร์ตี้เดียวกันตามลําดับเดียวกันได้อย่างมีประสิทธิภาพ หมวดหมู่นี้มีคลาสที่ซ่อนอยู่ซึ่งเรียกว่า system / Map
(ไม่เกี่ยวข้องกับ JavaScript Map
) และข้อมูลที่เกี่ยวข้อง
(sliced string)
เมื่อ V8 ต้องใช้สตริงย่อย เช่น เมื่อโค้ด JavaScript เรียก String.prototype.substring()
ทาง V8 อาจเลือกจัดสรรออบเจ็กต์สตริงที่ตัดแทนการคัดลอกอักขระที่เกี่ยวข้องทั้งหมดจากสตริงเดิม ออบเจ็กต์ใหม่นี้มีพอยน์เตอร์ไปยังสตริงต้นฉบับและอธิบายช่วงอักขระจากสตริงต้นฉบับที่จะใช้
จากมุมมองของโค้ด JavaScript สตริงเหล่านี้เป็นเพียงสตริงปกติและจะทํางานเหมือนสตริงอื่นๆ หากสตริงที่ตัดเก็บหน่วยความจําไว้มาก โปรแกรมอาจทริกเกอร์ปัญหา 2869 และอาจได้รับประโยชน์จากการดําเนินการอย่างตั้งใจเพื่อ "ยุบ" สตริงที่ตัด
system / Context
ออบเจ็กต์ภายในประเภท system / Context
มีตัวแปรภายในจาก Closure ซึ่งเป็นขอบเขต JavaScript ที่ฟังก์ชันที่ฝังอยู่เข้าถึงได้
อินสแตนซ์ของฟังก์ชันแต่ละรายการมีพ้อยเตอร์ภายในไปยัง Context
ที่เรียกใช้เพื่อให้เข้าถึงตัวแปรเหล่านั้นได้ แม้ว่าออบเจ็กต์ Context
จะมองไม่เห็นจาก JavaScript โดยตรง แต่คุณก็ควบคุมออบเจ็กต์เหล่านั้นได้โดยตรง
(system)
หมวดหมู่นี้มีออบเจ็กต์ภายในต่างๆ ที่ยังไม่ได้จัดหมวดหมู่อย่างมีความหมาย
มุมมองการเปรียบเทียบ
มุมมองการเปรียบเทียบช่วยให้คุณค้นหาวัตถุที่รั่วไหลได้โดยเปรียบเทียบภาพนิ่งหลายภาพ เช่น การดำเนินการและการดำเนินการย้อนกลับ เช่น การเปิดและปิดเอกสาร ไม่ควรทิ้งออบเจ็กต์ไว้
วิธียืนยันว่าการดำเนินการบางอย่างไม่ได้สร้างการรั่วไหล
- ถ่ายสแนปชอตฮีปก่อนดำเนินการ
- ดําเนินการ กล่าวคือ โต้ตอบกับหน้าเว็บในลักษณะที่คุณคิดว่าอาจทำให้เกิดการรั่วไหล
- ดำเนินการย้อนกลับ กล่าวคือ ให้ทำสิ่งที่ตรงกันข้ามและทําซ้ำ 2-3 ครั้ง
- ถ่ายภาพสแนปชอตกองขยะภาพที่สองและเปลี่ยนมุมมองเป็นการเปรียบเทียบ โดยเปรียบเทียบกับภาพสแนปชอต 1
มุมมองการเปรียบเทียบจะแสดงความแตกต่างระหว่างภาพรวม 2 รายการ เมื่อขยายรายการผลรวม อินสแตนซ์ออบเจ็กต์ที่เพิ่มและลบแล้วจะแสดงดังนี้
มุมมองการเก็บไว้
มุมมองการบรรจุคือ "ภาพรวม" ของโครงสร้างออบเจ็กต์ของแอปพลิเคชัน ซึ่งจะช่วยให้คุณดูข้อมูลภายในฟังก์ชันที่ปิดอยู่ สังเกตออบเจ็กต์ภายในของ VM ที่ประกอบกันเป็นออบเจ็กต์ JavaScript และทําความเข้าใจจํานวนหน่วยความจําที่แอปพลิเคชันใช้ในระดับที่ต่ำมาก
มุมมองนี้มีจุดแรกเข้าหลายจุด ดังนี้
- ออบเจ็กต์ DOMWindow ออบเจ็กต์ส่วนกลางสําหรับโค้ด JavaScript
- รูท GC รูท GC ที่โปรแกรมเก็บขยะของ VM ใช้ รูท GC อาจประกอบด้วยแผนที่ออบเจ็กต์ในตัว ตารางสัญลักษณ์ สแต็กเธรด VM แคชการคอมไพล์ ขอบเขตแฮนเดิล และแฮนเดิลส่วนกลาง
- ออบเจ็กต์เนทีฟ "ผลัก" ออบเจ็กต์เบราว์เซอร์ภายในเครื่องเสมือน JavaScript เพื่ออนุญาตการทำงานอัตโนมัติ เช่น โหนด DOM และกฎ CSS
ส่วนเครื่องมือเก็บ
ส่วนตัวยึดที่ด้านล่างของแผงความทรงจำจะแสดงออบเจ็กต์ที่ชี้ไปยังออบเจ็กต์ที่เลือกในมุมมอง แผงความทรงจำจะอัปเดตส่วนตัวยึดเมื่อคุณเลือกวัตถุอื่นในมุมมองใดก็ตาม ยกเว้นสถิติ
ในตัวอย่างนี้ พร็อพเพอร์ตี้ x
ของอินสแตนซ์ Item
จะเก็บสตริงที่เลือกไว้
ละเว้นเครื่องมือเก็บ
คุณสามารถซ่อนตัวยึดเพื่อดูว่าวัตถุอื่นๆ ยึดวัตถุที่เลือกไว้หรือไม่ เมื่อใช้ตัวเลือกนี้ คุณไม่จําเป็นต้องนําตัวยึดนี้ออกจากโค้ดก่อนแล้วจึงถ่ายสแนปชอตกองขยะอีกครั้ง
หากต้องการซ่อนตัวยึด ให้คลิกขวาแล้วเลือกละเว้นตัวยึดนี้ ระบบจะทําเครื่องหมายการคงที่ที่ละเว้นเป็น ignored
ในคอลัมน์ระยะทาง หากต้องการเลิกละเว้นตัวยึดทั้งหมด ให้คลิก Restore ignored retainers ในแถบการดำเนินการที่ด้านบน
ค้นหาวัตถุที่เฉพาะเจาะจง
หากต้องการค้นหาออบเจ็กต์ในกองข้อมูลที่รวบรวม คุณสามารถค้นหาได้โดยใช้ Ctrl + F แล้วป้อนรหัสออบเจ็กต์
ตั้งชื่อฟังก์ชันเพื่อแยกความแตกต่างของ Closure
การตั้งชื่อฟังก์ชันจะช่วยได้มากในการแยกแยะระหว่างการปิดท้ายในสแนปชอต
เช่น โค้ดต่อไปนี้ไม่ได้ใช้ฟังก์ชันที่มีชื่อ
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function() { // this is NOT a named function
return largeStr;
};
return lC;
}
แต่ตัวอย่างนี้
function createLargeClosure() {
var largeStr = new Array(1000000).join('x');
var lC = function lC() { // this IS a named function
return largeStr;
};
return lC;
}
ตรวจหาการรั่วไหลของ DOM
เครื่องมือวิเคราะห์ฮีปสามารถแสดงการพึ่งพาแบบ 2 ทิศทางระหว่างออบเจ็กต์ที่มาจากเบราว์เซอร์ (โหนด DOM และกฎ CSS) กับออบเจ็กต์ JavaScript ซึ่งจะช่วยค้นพบการรั่วไหลที่มองไม่เห็นซึ่งเกิดขึ้นเนื่องจากมี DOM ย่อยที่แยกออกมาซึ่งลืมทิ้งไว้
การเปิดเผย DOM อาจใหญ่กว่าที่คุณคิด ลองดูตัวอย่างต่อไปนี้ มีการเก็บขยะ #tree
เมื่อใด
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");
body.removeChild(treeRef);
//#tree can't be GC yet due to treeRef
treeRef = null;
//#tree can't be GC yet due to indirect
//reference from leafRef
leafRef = null;
//#NOW #tree can be garbage collected
#leaf
เก็บข้อมูลอ้างอิงถึงรายการหลัก (parentNode
) และรายการย่อยแบบซ้ำซ้อนสูงสุดถึง #tree
ดังนั้นเมื่อ leafRef
มีค่าเป็น 0 เท่านั้นที่ต้นไม้ทั้งหมดที่อยู่ภายใต้ #tree
จะเข้าเกณฑ์ GC