JVM Garbage Collection ทำงานอย่างไร
หลายคนเขียนโปรแกรมด้วย Java ก็จะรู้จักกับ Gargage Collection ที่ทำงานบน JVM ที่มีประโยชน์อย่างมากในการจัดการหน่วยความจำโดยอัตโนมัติ ในบทความนี้จะมาทำความเข้าใจเกี่ยวกับ Garbage Collection ของ Java กันมากยิ่งขึ้น
Garbage Collection
เป็นกระบวนการจัดการหน่วยความจำโดยอัตโนมัติของการเขียนโปรแกรมด้วย Java ซึ่งจะ compile จาก Java code ไปเป็น bytecode ที่ทำงานบน Java Virtual Machine (JVM) เมื่อโปรแกรมที่ทำงานบน JVM จะมีการสร้าง object บน Heap memory ซึ่งเป็นส่วนของหน่วยความจำที่จัดใว้ให้กับโปรแกรม จนท้ายที่สุดบาง object จะไม่ได้ใช้งาน ดังนั้น Garbage Collection จะหา object ที่ไม่ใช้งานเหล่านี้ และลบออกจากหน่ยวความจำเพื่อทำให้หน่วยความจำส่วนนั้นว่าง
How it work?
Java Garbage Collection เป็นกระบวนการอัตโนมัติ โดยที่โปรแกรมเมอร์ไม่จำเป็นต้องระบุว่า object ไหนจะต้องลบ ซึ่งจะตรวจสอบที่ heap memory โดยที่จะระบุ object ว่าใช้งานหรือไม่ และลบ object ที่ไม่ได้ใช้งานออกไป จาก heap memory
Object (Used Object) ที่มีการใช้งานอยู่ หรือ มีการ reference อยู่ หมายความว่ามีบางส่วนของโปรแกรมยังคงมี pointer ไปยัง object นั้นอยู่
Object (Unused Object) ที่ไม่มีการใช้งาน หรือ ไม่มีการ reference เป็น object ที่ไม่มีการ reference มาหา object นั้นจากส่วนใดๆ ของโปรแกรม
ดังนั้นส่วนของ memory ที่ถูกใช้โดย object ที่ไม่มีการใช้งานก็ควรจะคืนให้กับระบบ
ในบางภาษาโปรแกรมอย่าง C การจองหน่วยความจำ (allocate) และคืนหน่วยความจำ (deallocate) โปรแกรมเมอร์จะต้องจัดการเอง แต่ในภาษา Java กระบวการคืนหน่วยความจำจะจัดการโดยอัตโนมัติ ด้วย Garbage Collector โดยมีกระบวนการง่ายๆ ดังนี้
Step1: Marking
ในขั้นตอนแรกของกระบวนการ เรียกว่า marking โดยที่ garbage collector จะระบุในแต่ละส่วนของหน่วยความจำว่าเป็น object ที่ใช้งานอยู่ หรือ ไม่มีการใช้งาน
ในรูปด้านบน object ที่ใช้งานอยู่แสดงด้วยสีฟ้า และ object ที่ไม่ได้ใช้งานแล้วแสดงด้วยสีส้ม โดยที่ object ทั้งหมดจะถูกตรวจสอบและ mark ว่าเป็นชนิดไหนเพื่อใช้ในการพิจารณาว่าจะลบหรือไม่ ซึ่งเป็นขั้นตอนที่อาจจะใช้เวลามากถ้าต้องตรวจสอบหน่วนความจำของทั้งระบบ
Step2: Normal Deletion
เป็นขั้นตอนต่อมาจากการ marking จะลบ object ที่ไม่ใช้งานออกไปจากหน่วยความจำ โดยที่เหลือไว้เฉพาะ object ที่ใช้งานอยู่ ซึ่งส่วนที่ลบออกไปจะเป็นหน่วยความจำที่ว่าง (free space)
ซึ่งตัวจัดการหน่วยความจำ (memory allocator) จะจำตำแหน่งของความจำที่ว่างไว้ และ เมื่อโปรแกรมมี object ใหม่ ที่ต้องการใช้งานหน่วยความจำก็จะจองหน่วยความจำส่วนนี้ให้
Step2a: Deletion with Compacting
เพื่อเพิ่มประสิทธิภาพการทำงาน หลังจากที่ลบ object ที่ไม่ได้ใช้งานออกไปแล้ว ก็จะทำการ compact ส่วนที่เหลือ โดยการย้าย object ที่ใช้งานอยู่ไปอยู่ในตำแหน่งที่ใกล้กัน จะทำให้ตัวจัดการหน่วยความจำ ทำการจองหน่วยความจำให้กับ object ใหม่ของโปรแกรมได้ง่ายและรวดเร็วกว่า
JVM Generation
การเรียนรู้จากพฤติกรรมการจองหน่วยความจำของ object (object allocation) สามารถใช้ในการเพิ่มประสิทธิภาพของ JVM ได้ ใน Hotspot Garbage Collection แบ่ง heap memory เป็นส่วนเล็กๆ หรือ generation ซึ่งส่วนของ heap คือ Yong Generation, Old หรือ Tenured Generation และ Permanent Generation
- Yong Generation — เป็นส่วนของ heap ที่เพิ่งจองหน่วยความจำให้ object ใหม่
- Old Generation — เป็นส่วนที่เก็บ object ที่มีการใช้งานนานพอสมควร โดยที่ย้ายมาจาก Yong Generation ซึ่งส่วนนี้มีมากจนเรียกว่าเป็น major garbage collection.
- Permanent Generation — เป็นส่วนที่เก็บ meta data ที่ใช้โดย JVM สำหรับ class หรือ method ที่ใช้ในโปรแกรม
Type of Garbage Collection
ใน Hotspot JVM มีชนิดของ Garbage Collection มี 4 แบบดังนี้
Serial GC
Serial Garbage Collector เป็นชนิด default ใน Java 5/6 ซึ่งทั้ง minor และ major garbage collection ทำงานในแบบ serial ใช้ Virtual CPU เดียว โดยใช้วิธี mark-compact collection ซึ่งวิธีนี้จะย้ายส่วนของหน่วยความจำเก่าไปที่ส่วนเริ่มต้นของ heap โดยที่จะจองหน่วยความจำใหม่จะจองต่อเนื่องเรื่อยๆ จนกระทั่งหมดความจำ heap การ compact ของหน่วยความจำทำให้จองพื่นที่หน่วยความจำใหม่ได้ง่าย
สามารถใช้งาน Serial Garbage Collector ได้ด้วยคำสั่ง
-XX:+UseSerialGC
Parallel GC
Parallel Garbage Collector ใช้ multiple thread เพื่อที่จะทำ young generation garbage collection โดยค่าเริ่มต้นบน host ที่มี N CPUs ซึ่ง Parallel garbage collector ใช้ N garbage collector thread ในการ collection แต่จำนวนของ garbage collector thread สามารถควบคุมได้ด้วยคำสั่ง -XX:ParallelGCThreads=<จำนวน thread ที่ต้องการ>
สำหรับบนเครื่องที่มี CPU เดียว โดยค่าเริ่มต้นแล้วจะใช้ garbage collector เป็นคู่แม้ว่า parallel garbage collector ถูกเรียกใช้งาน ในเครื่องที่มี 2 CPU parallel garbage collector จะทำงานได้ดีกว่า
สามารถใช้งาน Parallel Garbage Collector ได้ด้วยคำสั่ง
-XX:+UseParallelGC
CMS GC
Concurrent Mark Sweep (CMS) collector จะ collect ในส่วนของ tenured generation โดยที่จะพยายามทำให้การหยุดเนื่องจาก garbage collection น้อยที่สุด โดยทำให้ garbage collection ทำงานแบบคู่ขนาน (Concurrently) กับ thread ของโปรแกรม ปรกติจะเป็น collector ที่หยุดน้อยมาก โดยจะไม่ copy หรือ compact object ที่ใช้งานอยู่ ซึ่งเป็น garbage collector ที่ทำงานโดยไม่ย้าย object ที่ใช้งานอยู่
Note: CMS collector บน young generation ใช้ algorithm เดียวกันกับ parallel collector
สามารถใช้งาน CMS Garbage Collector ด้วยคำสั่ง
-XX:+UseConcMarkSweepGC
และกำหนด thread ที่ใช้งานด้วย คำสั่ง
-XX:ParallelCMSThreads=<n>
G1 GC
Garbage First หรือ G1 garbage collector เริ่มใช้งานใน Java 7 โดยที่ออกแบบให้ใช้งานแทนที่ CMS collector ซึ่ง G1 collector เป็นการทำงานแบบ parallel, concurrent และ incrementally compacting low-pause garbage collector ที่มี layout แตกต่างเล็กน้อยจาก garbage collector ก่อนหน้านี้
สามารถใช้งาน G1 Garbage Collector ด้วยคำสั่ง
-XX:+UseG1GC
สรุป
การทำความเข้าใจ Garbage Collection ของ JVM นั้นทำให้เราสามารถเลือกใช้ Garbage Collector ได้ถูกต้องกับความต้องการและปรับปรุงประสิทธิภาพการทำงานของโปรแกรมได้อีกด้วย