- พบ อาการหน่วยความจำรั่วร้ายแรง ใน เทอร์มินัลอีมูเลเตอร์ Ghostty ที่เมื่อรันเป็นเวลานานอาจใช้หน่วยความจำหลายสิบ GB
- สาเหตุของปัญหาคือมีหน่วยความจำที่ไม่ถูกปลดปล่อยสะสมอยู่ เพราะไม่มีการเรียก
munmap ใน ตรรกะการนำหน้าหน่วยความจำแบบไม่มาตรฐานกลับมาใช้ใหม่ ของโครงสร้าง PageList
- เนื่องจาก Claude Code CLI สร้างเอาต์พุตกราฟหลายโค้ดพอยต์บ่อยครั้ง ทำให้มีการใช้หน้าหน่วยความจำแบบไม่มาตรฐานมากขึ้น และทำให้การรั่วไหลปรากฏในวงกว้าง
- การแก้ไขได้เปลี่ยนเป็น ไม่ใช้หน้าหน่วยความจำแบบไม่มาตรฐานซ้ำ และปลดปล่อยทันที พร้อมใช้ ฟีเจอร์ VM tag ของ macOS เพื่อติดตามและยืนยันการรั่วไหล
- การแก้ไขนี้ถูกประเมินว่าเป็นการแก้ปัญหาการรั่วไหลครั้งใหญ่ที่สุดของ Ghostty และมีกำหนดรวมอยู่ในรีลีสถัดไป (1.3)
ภาพรวมการรั่วไหลของหน่วยความจำใน Ghostty
- ผู้ใช้บางรายรายงานว่า Ghostty ใช้หน่วยความจำ มากกว่า 37GB หลังจากเปิดใช้งานเป็นเวลานาน
- การรั่วไหลนี้มีอยู่มาตั้งแต่ เวอร์ชัน 1.0 เป็นอย่างน้อย และช่วงหลังมานี้แอป CLI บางตัวตรงตามเงื่อนไขที่ทำให้ปัญหาถูกเปิดเผย
- การแก้ไขได้ ถูกรวมเข้า GitHub แล้ว และมีกำหนดรวมอยู่ใน nightly build และรีลีสทางการ 1.3
โครงสร้าง PageList และวิธีจัดการหน่วยความจำ
- Ghostty ใช้โครงสร้าง ลิงก์ลิสต์แบบเชื่อมสองทาง ชื่อ PageList เพื่อเก็บเนื้อหาในเทอร์มินัล
- แต่ละหน้าจะมีข้อมูลอย่างอักขระ สไตล์ และไฮเปอร์ลิงก์
- หน้าต่าง ๆ ถูกจัดสรรด้วย
mmap และนำกลับมาใช้ใหม่ผ่าน พูลของหน้าขนาดมาตรฐาน
- หน้าที่มีขนาดไม่เกินมาตรฐานจะถูกส่งกลับเข้าพูล
- หน้าขนาดไม่มาตรฐาน ต้องถูกปลดปล่อยโดยตรงด้วย
munmap
- ตัวโครงสร้างนี้เองทำงานได้ตามปกติ แต่เกิดการรั่วไหลจาก บั๊กในตรรกะการปรับแต่งประสิทธิภาพ
การปรับแต่ง scrollback และสาเหตุที่เกิดบั๊ก
- เมื่อ Ghostty เกิน
scrollback-limit จะมีการปรับแต่งโดย นำหน้าที่เก่าที่สุดกลับมาใช้ใหม่
- ทำให้เพิ่มประสิทธิภาพได้ด้วยการปรับเพียง pointer โดยไม่ต้องจัดสรรหน้าใหม่
- ปัญหาคือในกระบวนการนี้มีการ เปลี่ยนเฉพาะ metadata ของหน้าที่ไม่มาตรฐานให้เป็นขนาดมาตรฐาน แต่ปล่อยให้หน่วยความจำจริงคงเดิม
- เมื่อถึงเวลาปลดปล่อยในภายหลัง จึงถูกเข้าใจผิดว่าเป็นหน้ามาตรฐานและ ไม่มีการเรียก
munmap
- ส่งผลให้ หน้าที่ไม่มาตรฐานไม่ถูกปลดปล่อยและสะสมเพิ่มขึ้น จนกลายเป็นการรั่วไหลของหน่วยความจำขนาดใหญ่เมื่อรันระยะยาว
Claude Code กับการเปิดเผยการรั่วไหลในวงกว้าง
- Claude Code CLI สร้างเอาต์พุตกราฟหลายโค้ดพอยต์บ่อยครั้ง ทำให้ ความถี่ในการใช้หน้าที่ไม่มาตรฐานเพิ่มขึ้น
- อีกทั้งยังมีเอาต์พุต scrollback จำนวนมาก ทำให้การรั่วไหลสะสมได้รวดเร็ว
- ตามการออกแบบของ Ghostty หน้าที่ไม่มาตรฐานควรเกิดขึ้นไม่บ่อย แต่ด้วยลักษณะการทำงานของ Claude Code ทำให้ สามารถทำซ้ำการรั่วไหลจำนวนมากได้
- ผู้พัฒนาย้ำว่าบั๊กนี้ไม่ใช่ปัญหาของ Claude Code แต่เป็น ข้อบกพร่องในตรรกะภายในของ Ghostty
รายละเอียดการแก้ไข
การติดตามการรั่วไหลด้วย VM tag
- ใช้ ฟีเจอร์ VM tag ของ Mach kernel บน macOS เพื่อ กำหนดแท็กเฉพาะให้กับการจัดสรรหน่วยความจำของ PageList
- ระหว่างการดีบักจึงสามารถระบุพื้นที่หน่วยความจำของ Ghostty ได้อย่างชัดเจน
- ช่วยอย่างมากในการติดตามสาเหตุของการรั่วไหลและยืนยันผลการแก้ไข
- ด้วยฟีเจอร์นี้จึงสามารถ ตรวจสอบด้วยสายตาได้ว่าหน่วยความจำที่เกี่ยวข้องกับ PageList ถูกปลดปล่อยหรือไม่
ระบบป้องกันการรั่วไหลของหน่วยความจำใน Ghostty
- Ghostty มีระบบตรวจจับและป้องกันการรั่วไหลหลายรูปแบบ
- ใช้ ตัวจัดสรรหน่วยความจำสำหรับตรวจจับการรั่วไหลของ Zig ในดีบักบิลด์และยูนิตเทสต์
- รันการทดสอบทั้งหมดใน CI ด้วย valgrind
- ตรวจสอบการรั่วไหลของ โค้ด Swift ด้วย macOS Instruments
- PR ที่เกี่ยวข้องกับ GTK จะถูกตรวจสอบด้วย การทดสอบ GUI ผ่าน Valgrind
- การรั่วไหลครั้งนี้เกิดขึ้น เฉพาะในเงื่อนไขบางแบบ จึงไม่สามารถทำซ้ำได้ด้วยการทดสอบเดิม
- มีการเพิ่มเคสทดสอบใหม่เพื่อ ป้องกันการเกิดซ้ำของบั๊ก
บทสรุป
- ปัญหานี้ได้รับการยืนยันว่าเป็นกรณี หน่วยความจำรั่วขนาดใหญ่ที่สุด ที่พบใน Ghostty
- หลังการแก้ไขจะยังคงมีการติดตามอย่างต่อเนื่องผ่าน รายงานจากผู้ใช้และการทดสอบที่ทำซ้ำได้
- ข้อมูลเพื่อการวินิจฉัยและกรณีที่ทำซ้ำได้ จากชุมชนมีบทบาทสำคัญอย่างยิ่งในการแก้ปัญหา
- ย้ำว่าการมีสภาพแวดล้อมที่สามารถทำซ้ำปัญหาได้คือ หัวใจสำคัญของการแก้หน่วยความจำรั่ว
1 ความคิดเห็น
ความเห็นจาก Hacker News
เป็นข่าวที่น่ายินดีมาก ขอปรบมือให้ทุกคนที่มีส่วนร่วมในการแก้ปัญหานี้
บั๊กนี้ถูกพูดถึงไปแล้วในเธรดนี้เมื่อสัปดาห์ก่อน
ดูเหมือนว่า Claude Code จะเป็นตัวกระตุ้นให้บั๊กนี้ไปกระทบผู้ใช้มากขึ้น แต่ก็มีคนอย่างผมที่ไม่ได้ใช้ Claude Code เลยและยังเจอปัญหาเดียวกัน
เกณฑ์ที่ทำให้หน้าถูกจัดว่าเป็น ‘ไม่มาตรฐาน (non-standard)’ นั้น ไม่ได้เป็นแบบขาวดำชัดเจน อย่างที่คิด
และผมคิดว่าปัญหารั่วอาจเกิดบ่อยขึ้นกับคนที่ใช้การตั้งค่าอย่าง
scrollback-limit = 0วิธีที่แก้ตอนนี้ก็อาจทำให้มีการลบแล้วสร้างหน้าที่ไม่มาตรฐานใหม่โดยไม่จำเป็น เลยแอบเสียดายว่าน่าจะรีไซเคิลหน้าเก่าที่เป็น non-standard อยู่แล้วได้หรือเปล่า
วิธีทำงานของ PageList ก็เหมือนเดิมมาตลอด และตอนมีบั๊กก็แค่ไปเห็นขนาดที่ผิดระหว่างการปรับความจุเท่านั้น
ไม่น่าจะมีการเปลี่ยนแปลงด้านประสิทธิภาพที่รู้สึกได้
ทางเลือกที่คุณเสนอมาก็เคยพิจารณาแล้ว แต่แนวทางปัจจุบันมี ข้อมูล benchmark รองรับเพียงพอ
ผมเองก็พร้อมจะเปลี่ยนความเห็นได้ แต่ครั้งนี้โฟกัสที่การแก้ memory leak มากกว่าการเปลี่ยนโลกทัศน์ทั้งชุด
มันเป็นบั๊กที่ทำให้เกิด segfault ได้จริงและรีโปรดิวซ์ได้
ในรอบ 20 ปีที่ผ่านมา ไม่มีอะไรทำให้ CLI ดูสดใหม่ได้เท่านี้เลย
เป็นบทความที่ยอดเยี่ยมมาก ขอบคุณ mitchellh ที่สร้าง Ghostty ขึ้นมา
ผมย้ายมาใช้ตั้งแต่ปีที่แล้วและไม่เคยเสียใจเลย
แต่ก็แปลกใจนิดหน่อยที่แพตช์นี้จะถูกรวมอยู่ในฟีเจอร์รีลีสอีกหลายเดือนข้างหน้า
นึกว่าจะเข้า bugfix release
พอเริ่มพูดถึง page ผมก็คิดทันทีว่า “อ๋อ เป็น memory pooling” แล้วต่อด้วย “น่าจะเป็น ring buffer” และก็ใช่จริง ๆ คือการ นำ scrollback กลับมาใช้ซ้ำ
ผมเดาตำแหน่งบั๊กได้เลยเหมือนกัน — เป็นส่วนที่ไม่ได้ free หน่วยความจำของ page อย่างถูกต้อง
ไดอะแกรมการจัดแนวหน่วยความจำก็ทำได้ดีมาก
มันย้ำเตือนอีกครั้งว่าทุกครั้งที่ลองทำอะไรใหม่ ๆ ก็มี โอกาสเกิด leak ได้เสมอ
ผมเพิ่งย้ายมา Ghostty ในสัปดาห์นี้ และระหว่างพัฒนาแอป terminal UI ก็เจอ OOM crash
โครงสร้างของมันใช้ไอคอน UTF8 ใน tab bar และพอ resize terminal ก็ crash ทันที
รีโปรดิวซ์ได้ง่ายมากจนกำลังจะเตรียม bug report อยู่แล้ว แต่ดูเหมือนจะคล้ายกับปัญหาที่อธิบายในบล็อกโพสต์มาก
หวังว่าจะได้รับการแก้ไข
ผมถาม @mitchellh ว่าใช้เครื่องมืออะไรทำ visualization หน่วยความจำ และเว็บก็ทำงานบนมือถือได้ดีมาก เลยอยากรู้ว่า stack ที่ใช้คืออะไร
โค้ดสำหรับ visualization เป็นงานใช้ครั้งเดียว เลยตรวจแค่ ความถูกต้อง มากกว่าคุณภาพ
แยก namespace ตามแต่ละบล็อกโพสต์และไม่ได้เอากลับมาใช้ซ้ำ
แค่ตรวจว่ามันไม่ได้ทำอะไรแปลก ๆ (เช่น ขุดบิตคอยน์หรือข้อมูลลับรั่วไหล)
ประเด็นสำคัญคือการสื่อสารข้อมูล และไดอะแกรมแบบนี้ช่วยให้เนื้อหา เข้าใจง่ายขึ้นมาก
ผมยังติดตามการพัฒนา Ghostty อยู่เรื่อย ๆ
มันให้ความรู้สึก overengineering อยู่นิดหน่อย แต่บั๊กโพสต์มอร์เท็มแบบนี้มีคุณค่ามากสำหรับคนที่รักงานช่างฝีมือ
ถ้าเป็น terminal ที่เขียนด้วย Rust จะจัดการ implementation แบบนี้อย่างไรโดยไม่เสีย performance ก็น่าสนใจเหมือนกัน
ถึงจะไม่ค่อยรู้เรื่อง Ghostty หรือ terminal emulator มากนัก ก็ยังอ่านแล้วเข้าใจได้ง่าย
ประทับใจกับการอธิบายที่เข้าถึงง่ายและเป็นมิตร
ย้ำให้เห็นอีกครั้งว่าการมี bug report ที่รีโปรดิวซ์ได้ สำคัญแค่ไหน
กำลังรอว่าจะมีใครพูดว่า “ถ้าใช้ Rust เรื่องนี้คงไม่เกิดขึ้น” ไหม
Rust ไม่ได้รับประกัน ‘leak safety’ ในระดับภาษา
แม้แต่ safe Rust ก็ทำให้หน่วยความจำรั่วได้ — เพียงแต่นี่ไม่ใช่ปัญหาด้าน safety
แม้แต่ใน standard API ก็ยังมีการอนุญาตให้ leak อย่างชัดเจน เช่น Box::leak
Rust แค่ทำให้การสร้าง leak ที่ไม่ได้ตั้งใจ เกิดขึ้นได้ยากขึ้น ไม่ได้ป้องกันได้อย่างสมบูรณ์