ดูวิธีบันทึกฮีพสแนปชอตด้วยหน่วยความจำ > โปรไฟล์ > สแนปชอตฮีป และค้นหาหน่วยความจำรั่วไหล
เครื่องมือสร้างโปรไฟล์ฮีปแสดงการกระจายหน่วยความจำตามออบเจ็กต์ JavaScript ของหน้าเว็บและโหนด DOM ที่เกี่ยวข้อง ซึ่งคุณสามารถใช้สแนปชอตฮีป JS วิเคราะห์กราฟหน่วยความจำ เปรียบเทียบสแนปชอต และค้นหาหน่วยความจำรั่วไหล ดูข้อมูลเพิ่มเติมได้ที่โครงสร้างการรักษาออบเจ็กต์
ถ่ายสแนปชอต
วิธีถ่ายฮีพสแนปชอต
- ในหน้าที่ต้องการโปรไฟล์ ให้เปิดเครื่องมือสำหรับนักพัฒนาเว็บและไปที่แผงหน่วยความจำ
- เลือกประเภทการทำโปรไฟล์ radio_button_checked สแนปชอตฮีป จากนั้นเลือกอินสแตนซ์ VM ของ JavaScript แล้วคลิกบันทึกสแนปชอต
เมื่อแผงหน่วยความจำโหลดและแยกวิเคราะห์สแนปชอต แผงจะแสดงขนาดรวมของออบเจ็กต์ JavaScript ที่เข้าถึงได้ใต้ชื่อสแนปชอตในส่วน HEAP SNAPSHOTS
สแนปชอตจะแสดงเฉพาะออบเจ็กต์จากกราฟหน่วยความจำที่เข้าถึงได้จากออบเจ็กต์ส่วนกลาง การถ่ายสแนปชอตจะเริ่มต้นด้วยคอลเล็กชันขยะเสมอ
ล้างสแนปชอต
หากต้องการนำสแนปชอตทั้งหมดออก ให้คลิกบล็อก ล้างโปรไฟล์ทั้งหมด:
ดูสแนปชอต
หากต้องการตรวจสอบสแนปชอตจากมุมมองต่างๆ เพื่อวัตถุประสงค์ที่แตกต่างกัน ให้เลือกมุมมองใดมุมมองหนึ่งจากเมนูแบบเลื่อนลงที่ด้านบน
ดู | เนื้อหา | วัตถุประสงค์ |
---|---|---|
สรุป | ออบเจ็กต์ที่จัดกลุ่มตามชื่อตัวสร้าง | ใช้เพื่อค้นหาวัตถุและการใช้หน่วยความจำตามประเภท มีประโยชน์ในการติดตามการรั่วไหลของ DOM |
การเปรียบเทียบ | ความแตกต่างระหว่างสแนปชอต 2 รายการ | ใช้เพื่อเปรียบเทียบสแนปชอต 2 รายการ (หรือมากกว่า) ก่อนและหลังการดำเนินการ ตรวจหาการมีอยู่และสาเหตุของการรั่วไหลของหน่วยความจำโดยการตรวจสอบเดลต้าในหน่วยความจำที่ว่างอยู่และจำนวนการอ้างอิง |
การควบคุม | เนื้อหาฮีป | ให้มุมมองโครงสร้างวัตถุที่ดียิ่งขึ้น และช่วยวิเคราะห์วัตถุที่อ้างอิงในเนมสเปซส่วนกลาง (หน้าต่าง) เพื่อค้นหาสิ่งที่ยังคงอยู่ ใช้รายงานนี้เพื่อวิเคราะห์การปิดและเจาะลึกวัตถุในระดับต่ำ |
สถิติ | แผนภูมิวงกลมของการจัดสรรหน่วยความจำ | ดูขนาดจริงของส่วนหน่วยความจำที่จัดสรรให้กับโค้ด, สตริง, อาร์เรย์ JS, อาร์เรย์ที่พิมพ์ และออบเจ็กต์ระบบ |
มุมมองสรุป
ในตอนแรก ฮีพสแนปชอตจะเปิดขึ้นในมุมมองสรุปที่แสดงเครื่องมือสร้างในคอลัมน์ คุณขยายตัวสร้างเพื่อดูวัตถุที่สร้างอินสแตนซ์ได้
หากต้องการกรองตัวสร้างที่ไม่เกี่ยวข้องออก ให้พิมพ์ชื่อที่ต้องการตรวจสอบในตัวกรองชั้นเรียนที่ด้านบนของมุมมองสรุป
ตัวเลขข้างชื่อตัวสร้างจะระบุจำนวนวัตถุทั้งหมดที่สร้างขึ้นโดยตัวสร้าง มุมมองสรุปยังแสดงคอลัมน์ต่อไปนี้ด้วย
- ระยะทางแสดงระยะทางไปยังรากโดยใช้เส้นทางแบบง่ายที่สั้นที่สุดของโหนด
- ขนาดระดับตื้น แสดงผลรวมของขนาดระดับตื้นของวัตถุทั้งหมดที่สร้างขึ้นโดยตัวสร้างบางรายการ ขนาดส่วนตื้นคือขนาดของหน่วยความจำที่ตัวออบเจ็กต์ถือเอง โดยทั่วไปแล้ว อาร์เรย์และสตริงจะมีขนาดระดับตื้นขนาดใหญ่ ดูเพิ่มเติมเกี่ยวกับขนาดของวัตถุ
- ขนาดที่เก็บรักษาไว้จะแสดงขนาดสูงสุดที่เก็บรักษาไว้ในหมู่ออบเจ็กต์ชุดเดียวกัน ขนาดที่เก็บรักษาไว้คือขนาดของหน่วยความจำที่คุณเพิ่มพื้นที่ว่างได้ด้วยการลบออบเจ็กต์และทำให้เข้าถึงทรัพยากร Dependency ไม่ได้อีกต่อไป ดูเพิ่มเติมเกี่ยวกับขนาดของวัตถุ
เมื่อคุณขยายตัวสร้าง มุมมองสรุปจะแสดงอินสแตนซ์ทั้งหมดของตัวสร้าง อินสแตนซ์แต่ละรายการจะได้รับรายละเอียดตามขนาดที่ตื้นและคงตัวในคอลัมน์ที่เกี่ยวข้อง ตัวเลขหลังอักขระ @
คือรหัสที่ไม่ซ้ำกันของออบเจ็กต์ ซึ่งจะช่วยให้คุณเปรียบเทียบฮีพสแนปชอตสำหรับแต่ละออบเจ็กต์ได้
รายการพิเศษในสรุป
นอกจากการจัดกลุ่มตามตัวสร้างแล้ว มุมมองสรุปยังจัดกลุ่มออบเจ็กต์ตามข้อมูลต่อไปนี้ด้วย
- ฟังก์ชันในตัว เช่น
Array
หรือObject
- ฟังก์ชันที่คุณกำหนดในโค้ด
- หมวดหมู่พิเศษที่ไม่ได้อิงตามตัวสร้าง
(array)
หมวดหมู่นี้มีออบเจ็กต์ที่คล้ายกับอาร์เรย์ภายในหลายรายการซึ่งไม่ตรงกับออบเจ็กต์ที่แสดงใน JavaScript โดยตรง
เช่น ระบบจัดเก็บเนื้อหาของออบเจ็กต์ Array
ของ JavaScript ไว้ในออบเจ็กต์รองภายในชื่อ (object elements)[]
เพื่อให้ปรับขนาดได้ง่ายขึ้น ในทํานองเดียวกัน พร็อพเพอร์ตี้ที่มีชื่อในออบเจ็กต์ JavaScript มักจัดเก็บไว้ในออบเจ็กต์ภายในรองที่ชื่อ (object properties)[]
ซึ่งแสดงอยู่ในหมวดหมู่ (array)
ด้วย
(compiled code)
หมวดหมู่นี้ประกอบด้วยข้อมูลภายในที่ V8 ต้องการเพื่อให้เรียกใช้ฟังก์ชันที่กำหนดโดย JavaScript หรือ WebAssembly ได้ แต่ละฟังก์ชันสามารถแสดงได้หลากหลาย ตั้งแต่ขนาดเล็กและช้า ไปจนถึงขนาดใหญ่และเร็ว
V8 จะจัดการการใช้หน่วยความจำในหมวดหมู่นี้โดยอัตโนมัติ หากฟังก์ชันทำงานหลายครั้ง V8 จะใช้หน่วยความจำมากขึ้นสำหรับฟังก์ชันนั้นเพื่อให้ทำงานได้เร็วขึ้น หากฟังก์ชันไม่ทำงานมาระยะหนึ่ง V8 อาจล้างข้อมูลภายในสำหรับฟังก์ชันนั้น
(concatenated string)
เมื่อ V8 เชื่อม 2 สตริงเข้าด้วยกัน เช่น ด้วยโอเปอเรเตอร์ JavaScript +
ระบบอาจเลือกแสดงผลลัพธ์เป็นการภายในเป็น "สตริงที่เชื่อมต่อ" หรือที่เรียกอีกอย่างว่าโครงสร้างข้อมูลเชือก
แทนที่จะคัดลอกอักขระทั้งหมดของสตริงแหล่งที่มา 2 รายการไปเป็นสตริงใหม่ V8 จะจัดสรรวัตถุขนาดเล็กที่มีช่องภายในชื่อ first
และ second
ซึ่งชี้ไปยังสตริงแหล่งที่มา 2 สตริง ซึ่งช่วยให้ V8 ประหยัดเวลาและหน่วยความจำได้ จากมุมมองของโค้ด JavaScript โค้ดเหล่านี้เป็นเพียงสตริงปกติและมีการทำงานเหมือนกับสตริงอื่นๆ
InternalNode
หมวดหมู่นี้แสดงออบเจ็กต์ที่จัดสรรภายนอก V8 เช่น ออบเจ็กต์ C++ ที่กำหนดโดย Blink
หากต้องการดูชื่อคลาส C++ ให้ใช้ Chrome สำหรับการทดสอบและดำเนินการต่อไปนี้
- เปิดเครื่องมือสำหรับนักพัฒนาเว็บ แล้วเปิดการตั้งค่า การตั้งค่า > การทดสอบ > ช่องทำเครื่องหมาย แสดงตัวเลือกเพื่อแสดงภายในในฮีปสแนปชอต
- เปิดแผงหน่วยความจำ เลือก radio_button_checked สแนปชอตฮีป แล้วเปิด radio_button_checked แสดงภายใน (รวมถึงรายละเอียดเพิ่มเติมเฉพาะสำหรับการใช้งาน)
- จำลองปัญหาที่ทำให้
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
มีตัวแปรภายในจากการปิด ซึ่งเป็นขอบเขต JavaScript ที่ฟังก์ชันที่ซ้อนกันเข้าถึงได้
ทุกอินสแตนซ์ของฟังก์ชันจะมีตัวชี้ภายในไปยัง Context
ที่อินสแตนซ์ที่เรียกใช้เพื่อให้เข้าถึงตัวแปรเหล่านั้นได้ แม้ว่าออบเจ็กต์ Context
จะไม่ปรากฏจาก JavaScript โดยตรง แต่คุณก็ควบคุมออบเจ็กต์เหล่านี้ได้โดยตรง
(system)
หมวดหมู่นี้มีออบเจ็กต์ภายในต่างๆ ที่ (ยัง) ยังไม่ได้รับการจัดหมวดหมู่ในวิธีที่มีความหมายมากขึ้น
มุมมองการเปรียบเทียบ
มุมมองการเปรียบเทียบช่วยให้คุณพบออบเจ็กต์ที่รั่วไหลด้วยการเปรียบเทียบสแนปชอตหลายรายการเข้าด้วยกัน ตัวอย่างเช่น เมื่อคุณดำเนินการอย่างใดอย่างหนึ่งแล้วสลับกลับ เช่น เปิดเอกสารแล้วปิดเอกสารนั้น ไม่ควรปล่อยให้มีวัตถุอื่นๆ เกินมา
วิธีตรวจสอบว่าการดำเนินการบางอย่างไม่ทำให้เกิดการรั่วไหล
- ถ่ายฮีพสแนปชอตก่อนดำเนินการ
- ดำเนินการ กล่าวคือ ให้โต้ตอบกับหน้าเว็บในลักษณะที่คุณคิดว่าอาจเป็นสาเหตุของการรั่วไหล
- ดำเนินการกลับด้าน กล่าวคือ ให้โต้ตอบตรงกันข้าม แล้วทำซ้ำ 2-3 ครั้ง
- ถ่ายฮีพสแนปชอตที่ 2 และเปลี่ยนมุมมองเป็นการเปรียบเทียบ แล้วเปรียบเทียบกับสแนปชอต 1
มุมมองการเปรียบเทียบจะแสดงความแตกต่างระหว่างสแนปชอต 2 รายการ เมื่อขยายรายการทั้งหมด อินสแตนซ์ออบเจ็กต์ที่เพิ่มและลบจะแสดง
มุมมองการควบคุม
มุมมองคอนเทนเนอร์คือ "มุมมองจากมุมสูง" ของโครงสร้างออบเจ็กต์ของแอปพลิเคชัน ซึ่งช่วยให้คุณดูส่วนการปิดฟังก์ชันภายใน สังเกตออบเจ็กต์ภายใน VM ที่รวมกันเป็นออบเจ็กต์ JavaScript และทำความเข้าใจจำนวนหน่วยความจำที่แอปพลิเคชันใช้ในระดับที่ต่ำมาก
โดยมุมมองนี้จะมีจุดแรกเข้าหลายจุด
- ออบเจ็กต์DOMWindow ออบเจ็กต์ส่วนกลางสำหรับโค้ด JavaScript
- รูท GC รูท GC ที่เครื่องมือเก็บข้อมูลขยะของ VM ใช้ รูท GC อาจประกอบด้วยออบเจ็กต์แมปในตัว, ตารางสัญลักษณ์, สแต็กเทรด VM, แคชการคอมไพล์, ขอบเขตแฮนเดิล และแฮนเดิลส่วนกลาง
- ออบเจ็กต์เนทีฟ ออบเจ็กต์เบราว์เซอร์ "พุช" ภายในเครื่องเสมือน JavaScript เพื่ออนุญาตการทำงานอัตโนมัติ เช่น โหนด DOM และกฎ CSS
ส่วนเครื่องมือเก็บรักษา
ส่วนตัวเก็บรักษาที่ด้านล่างของแผงหน่วยความจำจะแสดงวัตถุที่ชี้ไปยังวัตถุที่เลือกในมุมมอง แผงหน่วยความจำจะอัปเดตส่วนที่เก็บเมื่อคุณเลือกออบเจ็กต์อื่นในมุมมองใดก็ตาม ยกเว้นสถิติ
ในตัวอย่างนี้ สตริงที่เลือกจะเก็บไว้โดยพร็อพเพอร์ตี้ x
ของอินสแตนซ์ Item
ละเว้นการเก็บรักษา
คุณจะซ่อนการเก็บรักษาได้เพื่อดูว่าออบเจ็กต์อื่นๆ ใดจะยังเก็บรายการที่เลือกไว้อยู่ ด้วยตัวเลือกนี้ คุณจะไม่ต้องนำตัวเก็บนี้ออกจากโค้ดก่อน แล้วจึงถ่ายฮีปสแนปชอตอีกครั้ง
หากต้องการซ่อนการเก็บรักษา ให้คลิกขวาแล้วเลือกละเว้นที่เก็บนี้ การเก็บรักษาที่ละเว้นจะทำเครื่องหมายเป็น ignored
ในคอลัมน์ระยะทาง หากต้องการหยุดละเว้นการเก็บรักษาทั้งหมด ให้คลิก playlist_remove กู้คืนการเก็บรักษาที่ละเว้นในแถบการดำเนินการด้านบน
ค้นหาวัตถุที่เจาะจง
หากต้องการค้นหาออบเจ็กต์ในฮีปที่เก็บรวบรวมไว้ คุณสามารถค้นหาโดยใช้ Ctrl + F แล้วป้อนรหัสออบเจ็กต์
ตั้งชื่อฟังก์ชันเพื่อแยกการปิด
การตั้งชื่อฟังก์ชันจะช่วยได้มาก เพราะคุณสามารถแยกความแตกต่างระหว่างการปิดต่างๆ ในสแนปชอตได้
ตัวอย่างเช่น โค้ดต่อไปนี้ไม่ใช้ฟังก์ชันที่มีชื่อ
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
เครื่องมือสร้างโปรไฟล์ฮีปมีความสามารถในการแสดงทรัพยากร Dependency แบบ 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
เป็นค่าว่างจะเป็นแผนผังทั้งหมดภายใต้ #tree
ของผู้สมัครชิงตำแหน่งสำหรับ GC เท่านั้น