คำศัพท์เกี่ยวกับหน่วยความจำ

Meggin Kearney
Meggin Kearney

ส่วนนี้จะอธิบายคำศัพท์ทั่วไปที่ใช้ในการวิเคราะห์หน่วยความจำ และเกี่ยวข้องกับเครื่องมือสร้างโปรไฟล์หน่วยความจำแบบต่างๆ สำหรับภาษาต่างๆ

ข้อกำหนดและความคิดเห็นที่อธิบายไว้ในที่นี้หมายถึงเครื่องมือสร้างโปรไฟล์ฮีปของ Chrome DevTools ถ้าคุณเคยทำงานกับ Java, .NET หรือเครื่องมือสร้างโปรไฟล์หน่วยความจำอื่นๆ นี่อาจเป็นการทบทวนบทเรียนนี้

ขนาดวัตถุ

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

การแสดงภาพความทรงจำ

ออบเจ็กต์เก็บหน่วยความจำได้ 2 วิธีดังนี้

  • โดยออบเจ็กต์โดยตรง
  • โดยปริยายโดยการอ้างอิงถึงวัตถุอื่นๆ เพื่อป้องกันไม่ให้อุปกรณ์เก็บขยะกำจัดวัตถุเหล่านั้นโดยอัตโนมัติ (เรียกสั้นๆ ว่า GC)

เมื่อทำงานกับเครื่องมือสร้างโปรไฟล์ฮีปใน DevTools (เครื่องมือสำหรับตรวจสอบปัญหาหน่วยความจำที่พบใน "โปรไฟล์") คุณอาจพบว่าตัวเองดูข้อมูล 2-3 คอลัมน์ 2 อย่างที่โดดเด่นคือ Shallow Size และขนาดที่ไม่คงตัว แล้วสิ่งเหล่านี้แสดงถึงอะไร

ขนาดระดับออบเจ็กต์และไม่เปลี่ยนแปลง

ขนาดระดับออบเจ็กต์

นี่คือขนาดของหน่วยความจำที่ออบเจ็กต์เก็บไว้

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

หน่วยความจำตัวแสดงผลคือหน่วยความจำทั้งหมดของกระบวนการที่มีการแสดงผลหน้าเว็บที่ตรวจสอบ: หน่วยความจำดั้งเดิม + หน่วยความจำฮีป JS ของหน้า + หน่วยความจำฮีป JS ของผู้ปฏิบัติงานเฉพาะทั้งหมดที่เริ่มต้นโดยหน้าเว็บ อย่างไรก็ตาม แม้แต่วัตถุขนาดเล็กก็สามารถเก็บหน่วยความจำได้จำนวนมากโดยทางอ้อม ด้วยการป้องกันไม่ให้วัตถุอื่นๆ ถูกกำจัดทิ้งโดยกระบวนการจัดเก็บขยะโดยอัตโนมัติ

ขนาดที่คงไว้

นี่คือขนาดของหน่วยความจำที่จะว่างเมื่อออบเจ็กต์ถูกลบ รวมถึงออบเจ็กต์อ้างอิงของรูท GC ที่เข้าถึงไม่ได้

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

มีรูท GC ภายในมากมายที่ส่วนใหญ่ผู้ใช้ไม่สนใจ ในแง่ของแอปพลิเคชันมีแหล่งที่มาดังต่อไปนี้

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

กราฟหน่วยความจำเริ่มต้นด้วยรูทซึ่งอาจเป็นออบเจ็กต์ window ของเบราว์เซอร์หรือออบเจ็กต์ Global ของโมดูล Node.js คุณไม่ได้ควบคุมวิธี GC กับออบเจ็กต์รูทนี้

ควบคุมออบเจ็กต์รากไม่ได้

อะไรก็ตามที่ไม่สามารถเข้าถึงได้ตั้งแต่รูทจะได้รับ GC

ออบเจ็กต์ที่เก็บโครงสร้าง

ฮีปคือเครือข่ายของออบเจ็กต์ที่เชื่อมต่อถึงกัน ในโลกคณิตศาสตร์ โครงสร้างนี้เรียกว่ากราฟหรือกราฟหน่วยความจำ กราฟสร้างขึ้นจากโหนดที่เชื่อมต่อกันด้วยขอบ ซึ่งทั้ง 2 อย่างมีป้ายกำกับ

  • โหนด (หรือออบเจ็กต์) มีการติดป้ายกำกับโดยใช้ชื่อของฟังก์ชันตัวสร้างที่ใช้ในการสร้างโหนด
  • Edges มีการติดป้ายกำกับโดยใช้ชื่อของพร็อพเพอร์ตี้

ดูวิธีบันทึกโปรไฟล์โดยใช้เครื่องมือสร้างโปรไฟล์ฮีป สิ่งที่สะดุดตาบางส่วนที่เราเห็นในการบันทึกฮีปโปรไฟล์ ด้านล่างนี้ได้แก่ ระยะทางจากราก GC หากวัตถุเกือบทั้งหมดที่เป็นประเภทเดียวกันอยู่ในระยะเท่ากัน และมีวัตถุไม่กี่ชิ้นที่ไกลกว่า ก็ควรตรวจสอบให้ดี

ระยะห่างจากรูท

ผู้ควบคุม

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

ในแผนภาพด้านล่าง

  • โหนด 1 อยู่เหนือโหนด 2
  • โหนด 2 ครองโหนด 3, 4 และ 6
  • โหนด 3 อยู่เหนือโหนด 5
  • โหนด 5 อยู่เหนือโหนด 8
  • โหนด 6 อยู่เหนือโหนด 7

โครงสร้างแผนผังโดมิเนเตอร์

ในตัวอย่างด้านล่าง โหนด #3 เป็นตัวควบคุม #10 แต่ #7 ก็มีอยู่ในเส้นทางแบบง่ายทุกเส้นทางตั้งแต่ GC ไปยัง #10 ดังนั้น วัตถุ B จึงเป็นโดมิเนเตอร์ของวัตถุ A หากมี B อยู่ในเส้นทางง่ายๆ ทุกเส้นทางตั้งแต่รากถึงวัตถุ A

ภาพ Dominator แบบเคลื่อนไหว

ข้อมูลจำเพาะของ V8

เมื่อทำโปรไฟล์หน่วยความจำ เราขอแนะนำให้ทำความเข้าใจว่าทำไมฮีปสแนปชอตจึงมีประโยชน์ ส่วนนี้จะอธิบายหัวข้อเกี่ยวกับหน่วยความจำบางส่วนที่เกี่ยวข้องกับเครื่องเสมือน JavaScript V8 (V8 VM หรือ VM) โดยเฉพาะ

การแสดงออบเจ็กต์ JavaScript

ประเภทพื้นฐานมี 3 ประเภท ดังนี้

  • ตัวเลข (เช่น 3.14159..)
  • บูลีน (จริงหรือเท็จ)
  • สตริง (เช่น "Werner Heisenberg")

โดยจะอ้างอิงค่าอื่นๆ ไม่ได้และมักจะเป็น Leaf หรือสิ้นสุดโหนดเสมอ

ตัวเลขสามารถจัดเก็บเป็นอย่างใดอย่างหนึ่งต่อไปนี้

  • ค่าจำนวนเต็ม 31 บิตโดยทันทีที่เรียกว่าจำนวนเต็มขนาดเล็ก (SMI) หรือ
  • ออบเจ็กต์ฮีปหรือที่เรียกว่าเลขฮีป หมายเลขฮีปใช้สำหรับจัดเก็บค่าที่ไม่พอดีกับรูปแบบ SMI เช่น ดับเบิล หรือเมื่อต้องใส่กล่องค่า เช่น การตั้งค่าพร็อพเพอร์ตี้ในค่าดังกล่าว

คุณจัดเก็บสตริงได้ในตัวเลือกต่อไปนี้

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

หน่วยความจำสำหรับออบเจ็กต์ JavaScript ใหม่จะได้รับการจัดสรรจากฮีปของ JavaScript โดยเฉพาะ (หรือฮีป VM) วัตถุเหล่านี้มีการจัดการโดยพนักงานเก็บขยะของ V8 ดังนั้น วัตถุเหล่านี้จะยังมีชีวิตอยู่ตราบใดที่มีการอ้างอิงที่ชัดเจนถึงวัตถุอย่างน้อย 1 อย่าง

ออบเจ็กต์ดั้งเดิมคือสิ่งอื่นๆ ที่ไม่อยู่ในฮีป JavaScript ออบเจ็กต์ดั้งเดิมซึ่งแตกต่างจากออบเจ็กต์ฮีปไม่ได้จัดการโดยเครื่องมือเก็บขยะ V8 ตลอดอายุการใช้งาน และเข้าถึงได้จาก JavaScript โดยใช้ออบเจ็กต์ Wrapper ของ JavaScript เท่านั้น

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

เช่น หากต่อ a และ b คุณจะได้รับสตริง (a, b) ซึ่งแสดงผลลัพธ์ของการเชื่อมต่อ หากต่อ d ด้วยผลลัพธ์นั้นในภายหลัง คุณจะได้รับสตริงข้อเสียอีก ((a, b), d)

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

ออบเจ็กต์ JavaScript โดยทั่วไปสามารถเป็นอาร์เรย์ 1 ใน 2 ประเภทที่ใช้สำหรับจัดเก็บ

  • พร็อพเพอร์ตี้ที่มีชื่อ และ
  • องค์ประกอบตัวเลข

ในกรณีที่พร็อพเพอร์ตี้มีจำนวนน้อยมาก ระบบอาจจัดเก็บพร็อพเพอร์ตี้ดังกล่าวไว้ภายในในออบเจ็กต์ JavaScript โดยตรง

แมป - วัตถุที่อธิบายชนิดของวัตถุและเลย์เอาต์ ตัวอย่างเช่น แผนที่ใช้ในการอธิบายลำดับชั้นของออบเจ็กต์โดยนัยสำหรับการเข้าถึงพร็อพเพอร์ตี้ด่วน

กลุ่มออบเจ็กต์

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

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