Skip to main content
เทคโนโลยี

Memory Leak ปัญหาที่ developer ไม่ควรมองข้าม

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

เขียนโดย
Thanawat Udchachon
Internship @ borntoDev

Memory Leak คืออะไรและเกิดขึ้นได้อย่างไร

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

ส่วนของสาเหตุนั้นก็เกิดขึ้นได้จากหลายอย่างดังนี้ครับ

 

มีการ Reference ถึงอยู่แม้ไม่ได้ใช้แล้ว

มีตัวแปรที่ถูกสร้างขึ้นมาแล้วเมื่อใช้งานเสร็จแล้วไม่ได้คืน memory ไป แต่ในปัจจุบันปัญหานี้มักจะไม่ค่อยเกิดขึ้นแล้วครับเพราะมีสิ่งที่เรียกว่า Garbage Collector ซึ่งจะคอยคืนพื้นที่สำหรับตัวแปรที่ไม่ได้มีการใช้งานแล้ว เช่นมีการประกาศใช้ตัวแปรในฟังก์ชัน ที่เมื่อฟังก์ชันนั้นทำงานเสร็จแล้ว Garbage Collector ก็จะคืน memory ตรงนั้นไป แต่ถึงจะมีตัวช่วยแล้วก็ยังมีสิ่งที่นักพัฒนาควรคำนึกถึงด้วยครับคือ ถ้าหากเราเขียนสร้างตัวแปรไว้แล้วมีฟังก์ชันที่จะเรียกใช้งานตัวแปรเหล่านั้นในฟังก์ชัน เช่นการ set ค่าตัวแปรเหล่านั้น Garbage Collector ก็จะยังไม่ทำการคืน memory ในส่วนของตัวแปรนั้นได้ครับเพราะยังคงมีการอ้างถึงอยู่ ถึงแม้ว่าจริง ๆ แล้วจะไม่ได้มีการใช้งานตัวแปรนั้นแล้วก็ตาม ฉนั้นเวลาจะสร้างตัวแปรก็ควรคำนึกถึงที่ที่เหมาะสมสำหรับการประกาศตัวแปรด้วย

 

Re-Render ข้อมูลเดิม

การ Re-Render ข้อมูลเดิมซ้ำไปซ้ำมาทุกครั้งที่มีการเปลี่ยนแปลโดยไม่ได้ตั้งใจมักจะเป็นส่วนที่เกิดขึ้นแล้วเราไม่ทันจะได้สังเกตกัน เช่นใน React ที่เราอาจจะมี get เวลาเพื่อนำไปแสดง ซึ่งบางทีมันก็จะคอยอัปเดตตัวเองทุกครั้งที่มีการเปลี่ยนแปลง แล้วยิ่งเรามีเยอะมันก็ยิ่งใช้ทรัพยากรมากขึ้นเท่านั้น ซึ่งสำหรับปัญหานี้เราสามารถแก้ได้โดยการใช้ useMemo หรือ useCallback ที่จะมีการจำค่าที่ต้องการเอาไว้ และจะมีการอัปเดตก็ต่อเมื่อมีการเปลี่ยนแปลงเกิดขึ้นกับข้อมูลที่เราตั้งไว้เท่านั้น โดยความแตกต่างสำหรับ hook สองตัวนี้คือ useMemo จะจำการเรนเดอร์ครั้งแรกของระบบหรือจนกว่าข้อมูลที่เราต้องการจะมีการเปลี่ยนแปลง (return ค่าออกมา) ส่วน useCallback จะจำการทำงานครั้งแรกไว้ (เป็น function ได้ ไม่จำเป็นต้อง return)

 

อัปเดตข้อมูลที่ unmounted ไปแล้ว

เป็นสาเหตุหนึ่งที่มักจะเกิดขึ้นสำหรับเว็บไซต์ที่มีการดึงข้อมูลใหญ่ ๆ จาก API โดยสถานการณ์ที่มักจะเกิดขึ้นคือ ผู้ใช้กดปุ่มที่จะมีการดึงข้อมูลจำนวนมากจาก API ซึ่งต้องใช้เวลาประมาณหนึ่งแล้วผู้ใช้เปลี่ยนหน้าก่อนที่จะดึงข้อมูลจาก API สำเร็จแล้วกำลังจะ update state แต่ component นั้นได้ unmounted ไปแล้ว (เปลี่ยนหน้าจากหน้าที่มีการดึงข้อมูลจาก API ไปหน้าอื่น) และนั่นแหละครับ ก็เกิดเป็น Memory Leak สำหรับการป้องกันไม่ให้เกิด Memory Leak ของ React สามารถทำได้ดังนี้ครับ

เชื่อว่าเพื่อน ๆ ที่ใช้ React มาเป็นเวลานานน่าจะคุ้นเคยกับ componentDidMount() และ componentDidUpdate() กับเป็นอย่างดี ซึ่งมันก็ได้รวมกันเป็น useEffect ในปัจจุบัน ซึ่งเมื่อเราต้องการจะ unmount เราก็สามารถใช้ componentWillUnmount() ได้ และ useEffect ก็สามารถทำแบบเดียวกันนี้ได้ครับ เรามาดูตัวอย่างกันเลยดีกว่า

const [users, setUsers] = useState([]);
const [isMounted, setIsMouted] = useState(true)

useEffect(() => {
  axios.get('url').then((res) => {
    if (isMounted) {
      setUsers(res.data);
  }
});
return () => {
  setIsMouted(false);
 };
}, []);

จากโค้ดก็คือเรามีการสร้างตัวแปรที่ชื่อว่า isMounted ที่คอยเก็บสถานะว่าระบบยัง mount อยู่หรือไม่ โดยถ้ายัง mount อยู่ เมื่อดึงข้อมูลจาก API สำเร็จแล้วก็จะสามารถ setUsers ได้ แต่ถ้าระบบ unmounted แล้ว ภายใน return ก็จะทำงานโดยมันก็จะ setIsMounted เป็น false ทำให้เมื่อข้อมูลจาก API มาถึงก็จะไม่ได้ไป setUsers นั่นเอง

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

useEffect(() => {
    let abortController = new AbortController();
// ดึงข้อมูลโดยใช้ async
return () => {
  abortController.abort();
  }
}, []);

การตรวจหาต้นเหตุของ Memory Leak

ถ้าหากระบบของเราก่อให้เกิด Memory Leak ไปแล้ว แล้วเราไม่รู้ว่าอะไรในโค้ดของเราที่ทำให้เป็นแบบนั้น และจะให้ไปนั่งไลดูทีละตัวก็ดูจะลำบาก แต่ไม่ต้องเป็นห่วงครับ เพราะ browser บางตัวมีตัวช่วยในการตรวจสอบการใช้ memory ได้

โดยในที่นี้จะยกตัวเองเป็นของ Chrome นะครับ เราสามารถเปิด Developer Tools (F12) แล้วกดไปที่แถบ Memory แล้วทำการ Take heap snapshot ที่ปุ่มซ้ายบนครับ (Heap คือพื้นที่ที่ CPU จอง memory ไว้สำหรับการทำงานของโปรแกรม)

หลังจากที่เราสร้าง Heap Snapshot เราก็จะได้ข้อมูลการใช้งานมาในลักษณะนี้

จากนั้นให้เราใช้เว็บไซต์นั้นไปสักระยะหนึ่งแล้วให้ Take heap snapshot อีกที แล้วในครั้งนี้เราก็จะนำเอา Snapshot ตัวที่ 2 มาเปรียบเทียบกับ Snapshot ตัวที่ 1 แล้วเราก็จะรู้ว่าอะไรที่เพิ่มขึ้นผิดปกติ (โดยดูจาก Delta) แล้วเราก็ค่อยไปลองไล่หาต้นเหตุนี้กันต่อครับ

 

สรุปสุดท้ายสิ่งที่ผู้อ่านจะได้รับ

สำหรับ Memory Leak นั้นเรียกได้ว่าเป็นปัญหาที่ไม่ควรมองข้ามไปอย่างยิ่งเลย ถ้าหากเราละเลยมันและปล่อยมันไว้ เมื่อะบบเราใหญ่ขึ้นแล้วปัญหามันเด่นชัดขึ้น เมื่อถึงจุดนี้การไปไล่หาสาเหตุในระบบก็จะทำให้เราใช้เวลาและ effort ไปเป็นอย่างมาก ดังนั้นหากเราคำนึงถึงและคอยตรวจสอบอย่างสม่ำเสมอก็จะช่วยห้องกันปัญหานี้ได้ครับ

 

อ้างอิงจาก

    1. ตามจับ Memory Leak ใน Node.js ด้วย Chrome Memory Profiling Tools, สืบค้นเมื่อ 29 เมษายน 2565 จาก: https://medium.com/dev-it/ตามจับ-memory-leak-ใน-node-js-ด้วย-chrome-memory-profiling-tools-6bbd03c88808
    2. การใช้งานและความแตกต่างระหว่าง useMemo และ useCallback ของ React Hooks, สืบค้นเมื่อ 2 กุมภาพันธ์ 2565 จาก: https://blog.2my.xyz/2021/08/14/react-hooks-usememo-usecallback/
    3. How to Fix Memory Leaks in React, สืบค้นเมื่อ 29 เมษายน 2565 จาก: https://www.loginradius.com/blog/engineering/how-to-fix-memory-leaks-in-react/
    4. เพิ่มความสามารถให้กับ Functional Component ด้วย React Hooks จาก React 16.7.0, สืบค้นเมื่อ 6 พฤษภาคม 2565 จาก: https://medium.com/@rennerwin/เพิ่มความสามารถให้กับ-functional-component-ด้วย-react-hooks-จาก-react-16-7-0-b41e94d3464d
    5. เรื่องของ เมโมรี่ Stack กับ Heap, สืบค้นเมื่อ 6 พฤษภาคม 2565 จาก: http://computer2know.blogspot.com/2016/04/stack-heap.html

หากคุณสนใจพัฒนา สตาร์ทอัพ แอปพลิเคชัน
และ เทคโนโลยีของตัวเอง ?

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

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • คุกกี้ที่จำเป็น
    เปิดใช้งานตลอด

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

  • คุกกี้สำหรับการติดตามทางการตลาด

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

บันทึกการตั้งค่า