Tutorial

รู้จักกับ Garbage Collection ใน Java

By Arnon Puitrakul - 17 มกราคม 2016

รู้จักกับ Garbage Collection ใน Java

เมื่อตอนที่ผมย้ายจากเขียน C แล้วมาเขียน Java ก็ทำให้สงสัยอยู่เรื่องนึงว่า "ใน Java มันมีคำสั่งที่ทำงานกับ Memory โดยตรงมั้ย ?" คำตอบที่ได้มาคือ ไม่มี เพราะ Principle ของตัว Java บอกว่า Robust + Secure เพราะฉะนั้น การที่ Java อนุญาติให้โปรแกรมเมอร์สามารถเข้าถึง Memory ได้โดยตรงอาจจะทำให้โปรแกรมของเรา พัง ได้ ดังนั้นตัว Java จึงต้องมีระบบตัวนึงที่เข้ามาช่วย Allocate และ Release Memory มันมีชื่อว่า Garbage Collection แต่ก่อนอื่นมาดูกันก่อนว่าปกติโปรแกรมที่รันผ่าน JVM จะจัดการ Memory ยังไง

JVM จัดการกับ Memory ยังไง ?

ก่อนที่เราจะไปรู้กันว่า รถขยะ ของเราทำงานกันอย่างไร เราจะต้องมารู้กันก่อนว่า ปกติแล้ว JVM จัดการ Memory ในเครื่องเวลารันอย่างไร
จากรูปด้านล่างเป็น Model คร่าว ๆ ในการ Allocate Memory ใน JVM โดยแบ่งออกเป็น 3 ส่วนใหญ่ ๆ นั่นคือ Heap, PremGen (Permanent Generation) และ Thread เรามาค่อย ๆ ดูกันทีล่ะส่วนกันดีกว่า

Garbage Collection 1

Heap เป็นส่วนที่เก็บ Object ที่สร้างมาจาก Class ต่าง ๆ ที่เราสั่งมันสร้างมาจากใน Code โดยจะแบ่งได้เป็นอีก 2 ประเภทย่อย นั่นคือ Old Generation และ New Generation โดยเราจะมาพูดถึงกันอีกครั้งตรง Garbage Collection เลยล่ะกัน เพราะว่า มันคือพระเอกของเราในวันนี้
PremGen หรือเราจะเรียกเต็ม ๆ ว่า Permanent Generation เป็นส่วนที่ใช้เก็บ แม่พิมพ์ ต่าง ๆ หรือ Class เพื่อรอให้โปรแกรมของเราเรียกใช้นั่นเอง โดยตัวมันเองอนุญาตให้ Thread อื่น ๆ สามารถเข้าถึงได้อีกด้วย (แหงแหละ ถ้า Thread อื่นเข้าถึงไม่ได้แล้วจะ ใช้ยังไง ?)
Thread อันนี้เป็น ส่วนของ Memory ที่ค่อนข้างที่จะเป็นส่วนตัวกว่าคนอื่นเขานิดนึง เพราะว่ามันจะเก็บข้อมูลที่ใช้ใน Thread ของตัวเองเท่านั้น ใครก็ห้ามยุ่ง Thread ใคร Thread มัน

Garbage Collection คืออะไร ?

มาถึงพระเอกของเราในวันนี้กันสักที เจ้าตัว Garbage Collection มันเป็นระบบตัวนึงที่อาศัยอยู่ใน JVM (Java Virtual Mechine) ที่คอยทำหน้าที่เป็น รถขยะ (ต่อจากนี้จะเรียกว่า รถขยะล่ะกันนะ !) มันจะคอยเก็บตัวแปร หรือ Object ที่ไม่ได้ถูกใช้แล้วออกไป เช่นถ้าโปรแกรมเรารัน Method นึงแล้วมี Local Variable อยู่ตัวนึง พอ Method นั้นรันจบ Garbage Collection ก็จะมารับ Local Variable ตัวนั้นออกไปและทำการคืนหน่วยความจำกลับสู่ระบบนั่นเอง
ขั้นตอนการทำงานของมันง่ายมาก ๆ อารมณ์เหมือนเครื่องกรองน้ำที่เขาเอามาขายกันเลย เริ่มแรกโปรแกรมของเราทำการสร้าง Object ขึ้นมา โดย Object ที่พึ่งสร้างไปก็จะอาศัยอยู่ในส่วนของ New Generation และก็จะเป็นแบบนี้เรื่อยไป
จนกระทั่งส่วนของ New Generation เต็มทีนี้ล่ะ Garbage Collection พระเอกของเราก็จะมาทำการเก็บกวาด Memory ที่อยู่ใน New Generation ออกไป โดยมันเข้าไปดูว่า Memory อันไหนที่ไม่ได้ใช้แล้วมันก็จะคืนให้ระบบทิ้งไป และส่วนที่ยังใช้อยู่มันก็จะย้ายไปที่ Old Generation ทันที เรื่องคร่าว ๆ ก็ประมาณนี้ล่ะ

ถังขยะนรกนี่จะทำงานเมื่อไหร่ ?

นึกถึงถังขยะบ้านพวกนายทั้งหลายน่ะ นายจะเอามันไปทิ้งเมื่อไหร่ล่ะ ? คำตอบคือ เมื่อใกล้มันเต็มไง มันถึงจะเอาไปทิ้ง ลองคิดต่อ ว่าถ้าเราต้องเอาขยะไปทิ้ง เราก็ต้องเสียเวลาเดินเอาไปทิ้งอีก ในคอมพิวเตอร์ก็เช่นกัน การที่ตัว JVM ของเรามันปล่อย Memory คืนให้ระบบ มันก็ย่อมต้องเสียเวลาเหมือนกัน
โดยปกติแล้วคุณ รถขยะของเรา นั้นจะทำงานอยู่ตลอดเลยนะ แต่ว่ามันจะถูกจัดให้อยู่ในส่วนงานที่ไม่สำคัญ (หรือก็คือ อู้อยู่) แต่เมื่อพอ Memory ใกล้เต็มเท่านั้นแหละ มันรีบลุกมาจัดการเลย แต่ถ้าไม่ทัน มันก็จะเกิด Exception ตัวนึง ที่เราเหล่าโปรแกรมเมอร์น่าจะเคยเจอมันอยู่บ่อย ๆ (มั้ง ?) นั่นคือ OutOfMemory นั่นเอง สะพรึงมั้ยล่ะ !!
อย่างที่บอกไปว่า การที่เราให้ ระบบเก็บขยะ เข้ามาทำงานนั้นก็จะเสียเวลาไป ที่เสียเวลาเป็นเพราะว่า เมื่อมันเข้ามาทำงานแล้ว ระบบจะจัดตัว ระบบเก็บขยะ ให้อยู่ในความสำคัญต้น ๆ เลย ทำให้ ตัวโปรแกรมของเราที่ควรจะรันไปเรื่อย ๆ กับต้องมา หยุดรอ ให้ รถขยะ ไปซะก่อน ทำให้เราเสียเวลานั่นเอง (ยิ่งเก็บเยอะ ก็ยิ่งเสียเวลานะ)
ถ้าถามว่า "ถ้ายิ่งเก็บเยอะยิ่งเสียเวลา เราก็ให้มันเก็บน้อย ๆ แต่บ่อยขึ้นสิ"
อันนี้เป็นความเชื่อที่ผิดนะ เพราะว่า ยิ่งถ้าเราปล่อย รถขยะ มาบ่อยขึ้นทำให้ ระบบที่ควรจะรันไปอย่างต่อเนื่องต้องมาชะงักกับ รถขยะ บ่อยขึ้นทำให้มันเซ็งมาก ๆ

สรุป

วันนี้เราก็ได้มาทำความรู้จักกับการจัดสรรหน่วยความจำใน JVM กันไป และรู้เพิ่มอีกอย่างว่า มันมีระบบอยู่ระบบนึงคอยจัดการกับ Memory ให้เราอยู่ข้างหลังนั่นคือ Garbage Collection ซึ่งเป็นระบบที่สำคัญมากกับการพัฒนาโปรแกรมขนาดใหญ่ในองค์กร หรือ แม้แต่ Android Application ด้วยก็เช่นกัน ถ้าไม่รู้เรื่องนี้ก็สามารถทำให้ App ที่เราสร้างออกมานั้นบรรลัยได้ ดันนั้นเรื่องนี้ถือว่าเป็นเรื่องที่มีความสำคัญมาก มาก มาก ใส่ดอกจันทร์สัก 20 ดอกได้เลย เพราะสำคัญจริง ๆ ในตอนหน้า เราจะมาดูกันว่า ลึก ๆ การทำงานของมันทำงานยังไง และเราจะนำสิ่งเหล่านี้มาทำให้โปรแกรมของเราดีขึ้นได้อย่างไร สำหรับวันนี้ สวัสดีครับ
สามารถดูข้อมูลเพิ่มเติมได้จาก Document ของ Java ตามลิงค์ด้านล่างได้เลย
Understanding Memory Management - Oracle

Read Next...

จัดการเรื่องแต่ละมื้อ แต่ละเดย์ด้วย Obsidian

จัดการเรื่องแต่ละมื้อ แต่ละเดย์ด้วย Obsidian

Obsidian เป็นโปรแกรมสำหรับการจด Note ที่เรียกว่า สารพัดประโยชน์มาก ๆ เราสามารถเอามาทำอะไรได้เยอะมาก ๆ หนึ่งในสิ่งที่เราเอามาทำคือ นำมาใช้เป็นระบบสำหรับการจัดการ Todo List ในแต่ละวันของเรา ทำอะไรบ้าง วันนี้เราจะมาเล่าให้อ่านกันว่า เราจัดการะบบอย่างไร...

Loop แท้ไม่มีอยู่จริง มีแต่ความจริงซึ่งคนโง่ยอมรับไม่ได้

Loop แท้ไม่มีอยู่จริง มีแต่ความจริงซึ่งคนโง่ยอมรับไม่ได้

อะ อะจ๊ะเอ๋ตัวเอง เป็นยังไงบ้างละ เมื่อหลายเดือนก่อน เราไปเล่าเรื่องกันขำ ๆ ว่า ๆ จริง ๆ แล้วพวก Loop ที่เราใช้เขียนโปรแกรมกันอยู่ มันไม่มีอยู่จริง สิ่งที่เราใช้งานกันมันพยายาม Abstract บางอย่างออกไป วันนี้เราจะมาถอดการทำงานของ Loop จริง ๆ กันว่า มันทำงานอย่างไรกันแน่ ผ่านภาษา Assembly...

Monitor การทำงาน MySQL ด้วย Prometheus และ Grafana

Monitor การทำงาน MySQL ด้วย Prometheus และ Grafana

นอกจากการทำให้ Application รันได้แล้ว อีกเรื่องที่สำคัญไม่แพ้กันคือการวางระบบ Monitoring ที่ดี วันนี้เราจะมาแนะนำวิธีการ Monitor การทำงานของ MySQL ผ่านการสร้าง Dashboard บน Grafana กัน...

เสริมความ"แข็งแกร่ง" ให้ SSH ด้วย fail2ban

เสริมความ"แข็งแกร่ง" ให้ SSH ด้วย fail2ban

จากตอนที่แล้ว เราเล่าในเรื่องของการ Harden Security ของ SSH Service ของเราด้วยการปรับการตั้งค่าบางอย่างเพื่อลด Attack Surface ที่อาจจะเกิดขึ้นได้ หากใครยังไม่ได้อ่านก็ย้อนกลับไปอ่านกันก่อนเด้อ วันนี้เรามาเล่าวิธีการที่มัน Advance มากขึ้น อย่างการใช้ fail2ban...