ฉันบังเอิญค้นพบว่าเมื่อปฏิกิริยาเกิดขึ้นในเซิร์ฟเวอร์เมื่อ node_env! = การผลิตมันจะทำให้เกิดการรั่วไหลของหน่วยความจำ ปัญหาเฉพาะ: https://github.com/facebook/react/issues/7406 ด้วยการใช้โหนดและ react isomorphism และเทคโนโลยีอื่น ๆ อย่างกว้างขวางปัญหาเช่นการรั่วไหลของหน่วยความจำด้านโหนดควรดึงดูดความสนใจของเรา เหตุใดโหนดจึงมีแนวโน้มที่จะรั่วไหลของหน่วยความจำและวิธีการแก้ไขปัญหาหลังจากเกิดขึ้น? ต่อไปนี้เป็นการแนะนำสั้น ๆ และตัวอย่าง
ก่อนอื่นโหนดขึ้นอยู่กับเครื่องยนต์ V8 และวิธีการจัดการหน่วยความจำสอดคล้องกับ V8 ต่อไปนี้เป็นการแนะนำสั้น ๆ เกี่ยวกับเอฟเฟกต์หน่วยความจำที่เกี่ยวข้องของ V8
ขีด จำกัด หน่วยความจำ V8
โหนดถูกสร้างขึ้นบน V8 และสามารถจัดสรรและจัดการวัตถุ JS ผ่าน V8 V8 มีข้อ จำกัด เกี่ยวกับการใช้หน่วยความจำ (ประมาณ 1.4 กรัมสำหรับระบบหน่วยความจำรุ่นเก่า 64 บิตประมาณ 0.7 กรัมสำหรับระบบ 32 บิตประมาณ 32MB สำหรับระบบหน่วยความจำ 64 บิตรุ่นใหม่และประมาณ 16MB สำหรับระบบ 32 บิต) ภายใต้ข้อ จำกัด ดังกล่าววัตถุหน่วยความจำขนาดใหญ่จะไม่สามารถทำงานได้ หากคุณสัมผัสขอบเขตนี้โดยไม่ได้ตั้งใจกระบวนการจะออก
สาเหตุ: V8 บล็อกตรรกะแอปพลิเคชัน JavaScript เมื่อทำการรวบรวมขยะแล้วทำการทดสอบตรรกะแอปพลิเคชัน JavaScript อีกครั้งจนกว่าการรวบรวมขยะจะสิ้นสุดลง พฤติกรรมนี้เรียกว่า "Stop-the-World" หากหน่วยความจำฮีปของ V8 คือ 1.5GB จะใช้เวลามากกว่า 50 มม. สำหรับ V8 ในการรวบรวมขยะขนาดเล็กและใช้เวลามากกว่า 1 วินาทีสำหรับคอลเล็กชั่นขยะที่ไม่ใช่การสร้าง
ตั้งค่าหน่วยความจำรุ่นใหม่และหน่วยความจำรุ่นเก่าเพื่อถอดรหัสขีด จำกัด ของหน่วยความจำเริ่มต้นโดยการตั้งค่าโหนด--max-space-space-size = xxx (หน่วย MB) และโหนด-Max-new-space-size = xxx (หน่วย KB)
องค์ประกอบ V8 HEAP
กอง V8 ไม่ได้ประกอบด้วยสองส่วน: รุ่นเก่าและรุ่นใหม่ กองสามารถแบ่งออกเป็นหลายภูมิภาค:
ประเภทรีไซเคิล GC
GC ที่เพิ่มขึ้น
ระบุว่าตัวเก็บขยะเก็บขยะ (เพิ่ม) ขยะเมื่อสแกนพื้นที่หน่วยความจำและล้างขยะในตอนท้ายของรอบการสแกน
GC ที่ไม่ใช่การสร้าง
เมื่อใช้ตัวเก็บขยะที่ไม่ใช่การสร้างขยะขยะจะว่างเปล่าทันทีที่มีการรวบรวม
ตัวเก็บขยะจะดำเนินการรวบรวมขยะสำหรับพื้นที่หน่วยความจำรุ่นใหม่พื้นที่ตัวชี้รุ่นเก่าและพื้นที่ข้อมูลรุ่นเก่า วัตถุแรกเข้าสู่หน่วยความจำรุ่นใหม่ที่ใช้พื้นที่น้อยลง วัตถุส่วนใหญ่จะล้มเหลวอย่างรวดเร็วและ GC ที่ไม่ได้รับการปรับเปลี่ยนรีไซเคิลหน่วยความจำจำนวนเล็กน้อยเหล่านี้โดยตรง หากวัตถุบางอย่างไม่สามารถรีไซเคิลได้เป็นระยะเวลาหนึ่งพวกเขาจะถูกป้อนเข้าไปในพื้นที่หน่วยความจำรุ่นเก่า พื้นที่นี้ดำเนินการ GC ที่เพิ่มขึ้นไม่บ่อยนักและใช้เวลานาน
แล้วหน่วยความจำจะรั่วไหลเมื่อไหร่?
เส้นทางการรั่วไหลของหน่วยความจำ
องค์ประกอบหน่วยความจำของโหนดส่วนใหญ่เป็นส่วนที่จัดสรรผ่าน V8 และส่วนที่จัดสรรโดยโหนดเอง ข้อ จำกัด หลักของคอลเล็กชั่นขยะของ V8 คือหน่วยความจำกองของ V8 เหตุผลหลักสำหรับการรั่วไหลของหน่วยความจำ: 1. แคช; 2. การบริโภคคิวไม่ทันเวลา 3. ขอบเขตไม่ได้เปิดตัว
การวิเคราะห์การรั่วไหลของหน่วยความจำ
ตรวจสอบการใช้หน่วยความจำ V8 (ยูนิตไบต์)
process.memoryusage (); {ress: 47038464, heaptotal: 34264656, heapused: 2052866}ress: ส่วนหน่วยความจำที่อยู่อาศัยของกระบวนการ
heaptotal, heapused: ข้อมูลหน่วยความจำ V8 Heap
ตรวจสอบการใช้หน่วยความจำระบบ (หน่วยไบต์)
os.totalmem()
OS.FreeMem ()
ส่งคืนหน่วยความจำระบบทั้งหมดและหน่วยความจำที่ไม่ได้ใช้งาน
ดูบันทึกการรวบรวมขยะ
node --trace_gc -e "var a = []; สำหรับ (var i = 0; i <10,0000000; i ++) {a.push (อาร์เรย์ใหม่ (100));}" >> gc.log // บันทึกคอลเลกชันขยะเอาต์พุต
Node -PROF // บันทึกประสิทธิภาพการทำงานของโหนดการทำงาน ใช้ windows-tick.processor เพื่อดู
เครื่องมือตรวจสอบวิเคราะห์
V8-Profiler จับภาพสแน็ปช็อตของหน่วยความจำ V8 HEAP และวิเคราะห์ CPU
Node-Heapdump คว้าสแน็ปช็อตของหน่วยความจำ V8 HEAP
การวิเคราะห์ Node-MRACE
Node-Memwatch ฟังสถานการณ์การรวบรวมขยะ
โหนด-เมมนาฬิกา
memwatch.on ('สถิติ', ฟังก์ชั่น (ข้อมูล) {console.log (ข้อมูล)}) memwatch.on ('รั่ว', ฟังก์ชั่น (ข้อมูล) {console.log (ข้อมูล)})เหตุการณ์สถิติ: ทุกครั้งที่มีการดำเนินการรวบรวมกองขยะกองเต็มเหตุการณ์สถิติจะถูกทริกเกอร์ เหตุการณ์นี้จะผ่านสถิติหน่วยความจำ
{"num_full_gc": 17, // จำนวนคอลเลกชันขยะเต็มซ้อน "num_inc_gc": 8, // จำนวนคอลเลกชันขยะที่เพิ่มขึ้นจำนวนเท่าใด "HEAP_COMPACTIONS": 8, // กี่ครั้งที่มีการจัดเรียงการ์ดรุ่นเก่า " "นาที": 2499912, // ขั้นต่ำ "สูงสุด": 2592568, // สูงสุด "usage_trend": 0 // แนวโน้มผู้ใช้}สังเกต num_full_gc และ num_inc_gc สะท้อนคอลเลกชันขยะ
เหตุการณ์การรั่วไหล: หากหน่วยความจำยังไม่ถูกปล่อยออกมาหลังจากเก็บรวบรวมขยะ 5 ครั้งติดต่อกันหมายความว่าการรั่วไหลของหน่วยความจำเกิดขึ้น เวลานี้เหตุการณ์การรั่วไหลจะถูกทริกเกอร์
{เริ่มต้น: ศุกร์, 29 มิ.ย. 2555 14:12:13 GMT, สิ้นสุด: ศุกร์, 29 มิ.ย. 2555 14:12:33 GMT, การเติบโต: 67984, เหตุผล:ฮีปการกระจายหน่วยความจำกองการแก้ไขปัญหาการแก้ไขปัญหาหน่วยความจำล้น
ด้านล่างเราใช้ตัวอย่างเพื่อสาธิตวิธีแก้ไขปัญหาการรั่วไหลของหน่วยความจำ:
ก่อนอื่นเราสร้างตัวอย่างที่ทำให้เกิดการรั่วไหลของหน่วยความจำ:
//app.jsvar app = reghed ('express') (); var http = reghed ('http'). เซิร์ฟเวอร์ (แอป); var heapdump = ต้องการ ('heapdump'); var leakbjs = []; ฟังก์ชั่น reakclass () {this.x = 1; <1000; function () {console.log ('ฟังบนพอร์ต 3000');});ที่นี่เราจำลองการรั่วไหลของหน่วยความจำโดยการตั้งค่าอาร์เรย์ที่เพิ่มขึ้นอย่างต่อเนื่องและไม่ดึง
ใช้โมดูล HEAP-Dump เพื่อบันทึกสแนปชอตหน่วยความจำอย่างสม่ำเสมอและนำเข้าสแน็ปช็อตผ่านโปรไฟล์เครื่องมือนักพัฒนา Chrome สำหรับการเปรียบเทียบและการวิเคราะห์
เราจะเห็นได้ว่าหลังจากเบราว์เซอร์เข้าถึง LocalHost: 3000 และรีเฟรชหลายครั้งขนาดของสแน็ปช็อตก็เพิ่มขึ้นและแม้ว่าจะไม่ได้ร้องขอ แต่ก็ไม่ลดลงแสดงว่ามีการรั่วไหลเกิดขึ้น
จากนั้นเรานำเข้าสแน็ปช็อตผ่านโปรไฟล์เครื่องมือนักพัฒนาโครเมี่ยม โดยการตั้งค่าการเปรียบเทียบให้เปรียบเทียบสแน็ปช็อตเริ่มต้นส่งคำขอแล้วส่งคำขอเพื่อส่งสแน็ปช็อตหน่วยความจำในสามขั้นตอนเหล่านี้ คุณสามารถพบว่าการรั่วไหลได้เพิ่มขึ้นในใหม่ทางด้านขวา เป็นบวกในเดลต้าเสมอหมายความว่ามันไม่ได้รีไซเคิล
สรุป
สำหรับการรั่วไหลของหน่วยความจำคุณสามารถใช้ memwatch ในการปลูกถ่ายหรือรายงาน process.memoryusage การใช้หน่วยความจำเพื่อตรวจสอบเป็นประจำและตั้งค่าเกณฑ์การเตือนสำหรับการตรวจสอบ
เมื่อพบการรั่วไหลของหน่วยความจำหากได้รับอนุญาตคุณสามารถเรียกใช้ Node-Heapdump ในเครื่องและใช้สแน็ปช็อตหน่วยความจำที่กำหนดเวลาเพื่อสร้าง และใช้สแน็ปช็อตเพื่อวิเคราะห์สาเหตุของการรั่วไหลผ่านโปรไฟล์โครเมี่ยม หากไม่สามารถแก้ไขข้อบกพร่องในท้องถิ่นให้ใช้ V8-profiler เพื่อเอาต์พุตสแน็ปช็อตหน่วยความจำบนเซิร์ฟเวอร์ทดสอบเพื่อเปรียบเทียบและวิเคราะห์ JSON (จำเป็นต้องมีการบุกรุกรหัส)
ภายใต้สถานการณ์ใดที่ควรพิจารณา MemWatch/Heapdump ถูกเปิดใช้งาน พิจารณาความถี่ของ heapdump เพื่อหลีกเลี่ยงการหมด CPU วิธีอื่น ๆ ในการตรวจจับการเติบโตของหน่วยความจำยังสามารถพิจารณาได้เช่นกระบวนการตรวจสอบโดยตรง memoryusage ()
ระวังการพิจารณาผิด ๆ การใช้หน่วยความจำระยะสั้นจะทำตัวเหมือนการรั่วไหลของหน่วยความจำ หากแอพของคุณใช้ CPU และหน่วยความจำจำนวนมากทันทีเวลาในการประมวลผลอาจครอบคลุมรอบการรวบรวมขยะหลายรอบและจากนั้น MemWatch อาจตัดสินว่ามันเป็นการรั่วไหลของหน่วยความจำ อย่างไรก็ตามในกรณีนี้เมื่อแอปของคุณใช้ทรัพยากรเหล่านี้การใช้หน่วยความจำจะลดลงสู่ระดับปกติ ดังนั้นจึงเป็นเรื่องสำคัญที่จะต้องทราบว่ามีการรายงานการรั่วไหลของหน่วยความจำอย่างต่อเนื่องและสามารถละเว้นการเตือนภัยทันทีหนึ่งหรือสองครั้ง