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

เมกกิน เคียร์นีย์
Meggin Kearney

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

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

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

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

การนำเสนอภาพความทรงจำ

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

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

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

ขนาดระดับความตื้นและที่เก็บรักษาไว้

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

ซึ่งก็คือขนาดของหน่วยความจำที่จัดเก็บโดยตัวออบเจ็กต์เอง

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

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

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

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

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

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

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

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

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

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

ต้นไม้ที่รักษาวัตถุ

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

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

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

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

โดมิเนเตอร์

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

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

  • โหนด 1 แทนที่โหนด 2
  • โหนด 2 แทนที่โหนด 3, 4 และ 6
  • โหนด 3 แทนที่โหนด 5
  • โหนด 5 แทนที่โหนด 8
  • โหนด 6 แทนที่โหนด 7

โครงสร้างต้นไม้โดมิเนเตอร์

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

ภาพโดมิเนเตอร์แบบเคลื่อนไหว

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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