Chúng tôi phát hiện bài dưới dựa trên bài 2017 của Nate Berkopec "Understanding Ruby GC through GC.stat". Phần nội dung bị plagiarized, chưa hay biết cho đến khi tác giả gốc nhắc nhở. Chúng tôi check plagiarism trước publish nhưng missed case này. Xin lỗi lớn Nate và readers!
Hiểu garbage collection (GC) trong Ruby vital để kiểm soát hoàn toàn performance app.
Bài này deep dive implement và customize GC trong Ruby.
Module Ruby Garbage Collector
Module GC là interface đến cơ chế mark-and-sweep GC của Ruby.
Chạy auto background khi cần, nhưng GC module cho phép gọi GC manually khi cần và insight vào GC cycles. Module cung cấp parameters điều chỉnh performance.
Phương thức thường dùng:
- start/garbage_collect: initiate GC cycle manually
- enable/disable: bật/tắt auto GC cycles. Trả về boolean success
- stat: list keys/values mô tả performance GC. Chi tiết section sau
Hiểu Các Tham Số Ruby Garbage Collector
Để hiểu GC internal, xem metrics của GC module. Chạy trên irb fresh boot:
puts GC.stat
Output dạng:
{
:count=>12, :heap_allocated_pages=>49, :heap_sorted_length=>49, :heap_allocatable_pages=>0,
:heap_available_slots=>19975, :heap_live_slots=>19099, :heap_free_slots=>876, :heap_final_slots=>0,
:heap_marked_slots=>16659, :heap_eden_pages=>49, :heap_tomb_pages=>0, :total_allocated_pages=>49,
:total_freed_pages=>0, :total_allocated_objects=>66358, :total_freed_objects=>47259,
:malloc_increase_bytes=>16216, :malloc_increase_bytes_limit=>16777216,
:minor_gc_count=>10, :major_gc_count=>2,
:remembered_wb_unprotected_objects=>191, :remembered_wb_unprotected_objects_limit=>312,
:old_objects=>16024, :old_objects_limit=>23556,
:oldmalloc_increase_bytes=>158824, :oldmalloc_increase_bytes_limit=>16777216
}
Chứa tất cả info về GC trong runtime. Chi tiết từng số:
Counts (Đếm)
{
:count=>12,
:minor_gc_count=>10,
:major_gc_count=>2,
}
GC counts - thông tin straight-forward. minor_gc_count và major_gc_count là số lần chạy từng loại GC.
2 loại GC trong Ruby:
- Minor GC: chỉ GC objects mới (survived ≤ 3 GC cycles)
- Major GC: GC tất cả objects, kể cả old objects (survived > 3 cycles)
count = sum của minor + major.
Track GC count hữu ích: xác định job/process nào trigger GCs và bao nhiêu lần. Không 100% accurate (multithreaded apps) nhưng starting point tốt để tìm memory leaks.
Heap Numbers: Slots Và Pages
{
# page numbers
:heap_allocated_pages=>49, :heap_sorted_length=>49, :heap_allocatable_pages=>0,
# slots
:heap_available_slots=>19975, :heap_live_slots=>19099, :heap_free_slots=>876, :heap_final_slots=>0,
:heap_marked_slots=>16659,
# Eden và Tomb pages
:heap_eden_pages=>49, :heap_tomb_pages=>0,
}
Heap = C data structure chứa references đến tất cả live Ruby objects. Heap page = memory slots, mỗi slot = 1 live Ruby object.
heap_allocated_pages: số heap pages hiện allocated (có thể empty, full, partial)heap_sorted_length: size thực heap chiếm trong memory. Khácheap_allocated_pagesvì length = độ dài pages gộp, không phải count. Ví dụ: alloc 10 pages, free 1 ở giữa → allocated_pages=9, sorted_length=10heap_allocatable_pages: count heaps Ruby owned, có sẵn khi cần
Slots:
heap_available_slots: total slots trong heap pagesheap_live_slots: số live objects trong memoryheap_free_slots: slots rỗng trong allocated heap pagesheap_final_slots: slots có objects với finalizers (Procs chạy khi object freed, giống destructors OOP)heap_marked_slots: count old objects (survived >3 GC cycles) + write-barrier unprotected objects
tomb_pages: pages không live objects → cuối cùng release về OS.
eden_pages: pages có ít nhất 1 live object → không release được về OS.
Monitor heap_free_slots nếu gặp memory bloat. Free slots > 250,000 thường chỉ controller actions alloc nhiều objects cùng lúc rồi free → permanently bloat Ruby process size.
Cumulative Numbers (Số Tích Lũy)
{
:total_allocated_pages=>49, :total_freed_pages=>0,
:total_allocated_objects=>66358, :total_freed_objects=>47259,
}
Cumulative/additive cho toàn đời process. Không bao giờ reset bởi GC, không thể giảm. Tự giải thích.
Garbage Collection Thresholds (Ngưỡng)
Để hiểu, cần biết khi nào GC triggered:
{
:malloc_increase_bytes=>16216, :malloc_increase_bytes_limit=>16777216,
:remembered_wb_unprotected_objects=>191, :remembered_wb_unprotected_objects_limit=>312,
:old_objects=>16024, :old_objects_limit=>23556,
:oldmalloc_increase_bytes=>158824, :oldmalloc_increase_bytes_limit=>16777216
}
Khác với assumption thường gặp (GC chạy fixed intervals), GC runs triggered khi Ruby hết memory space.
- Minor GC: khi hết
free_slots - Major GC: nếu vẫn ít free_slots sau minor GC, hoặc vượt ngưỡng: oldmalloc, malloc, old object count, hoặc write-barrier-unprotected (shady) count
malloc_increase_bytes: memory allocated ngoài heap. Object size > slot standard (~40 bytes) → Ruby malloc space riêng. Vượt malloc_increase_bytes_limit → major GC.
oldmalloc_increase_bytes: ngưỡng tương tự cho old objects.
old_objects: count object slots marked old. Vượt old_objects_limit → major GC.
remembered_wb_unprotected_objects: count objects không protected bởi write-barrier và trong remembered set.
Write-barrier: interface giữa Ruby runtime và objects, cho interpreter track references ngay khi tạo. C-extensions có thể tạo references mới không qua write-barrier → objects marked shady/write-barrier unprotected. Remembered set = list old objects có reference đến new object.
Tùy Chỉnh Ruby Garbage Collection Performance
Đã hiểu GC manage memory, xem options customize behavior:
RUBY_GC_HEAP_INIT_SLOTS RUBY_GC_HEAP_FREE_SLOTS RUBY_GC_HEAP_GROWTH_FACTOR RUBY_GC_HEAP_GROWTH_MAX_SLOTS RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR và các biến khác
Chi tiết tham số quan trọng:
RUBY_GC_HEAP_INIT_SLOTS: initial slots trên Ruby heap. Default 10000. Thay đổi nếu app initially allocates hầu hết objects.RUBY_GC_HEAP_FREE_SLOTS: minimum free slots phải có sau GC cycle. Default 4096. Chỉ dùng 1 lần tại runtime lúc heap growth đầu tiên.RUBY_GC_HEAP_GROWTH_FACTOR: factor heap growth. Default 1.8. Thay đổi ít ý nghĩa, Ruby đã aggressive heap growth. Giảm không khác biệt lớn vì heaps allocated on-demand modern interpreters.RUBY_GC_HEAP_GROWTH_MAX_SLOTS: max slots Ruby add vào heap cùng lúc. Default 0 = no limit. App alloc millions objects lifetime → cap parameter. Effect thấp trên GC time.RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR: force major GC khi total old objects > factor × old objects sau GC cuối. Tăng nếu nhiều objects trở thành unused sau vào old generation. Hiếm khi cần.RUBY_GC_MALLOC_LIMIT: min limit malloc calls new generation. Default 16 MB.RUBY_GC_MALLOC_LIMIT_MAX: max limit. Default 32 MB. Tăng nếu app dùng memory cao hơn TB. Cẩn thận không raise quá cao → higher peak memory. Tăng dần 4-8 MB.RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR: growth factor malloc limit new gen. Default 1.4. Tăng nếu app alloc memory chunks thay vì all-at-once.RUBY_GC_OLDMALLOC_LIMITvàRUBY_GC_OLDMALLOC_LIMIT_MAX: min/max malloc limits old generation. Default 16 MB và 128 MB.RUBY_GC_OLDMALLOC_LIMIT_GROWTH_FACTOR: growth factor limit này. Default 1.2. Cân nhắc thay đổi cùng new gen limits cho max effect.
Fine-tuning Garbage Collection Trong Ruby
Đã bàn cách customize GC module phổ biến để improve performance. Tuy nhiên tweaks có thể không work mọi case. Cần figure out memory usage pattern app trước khi decide customize gì.
Flip side: chạy automated test tìm best values parameters. Tools như TuneMyGC straightforward tìm best set env variables.
Chắc chắn xem GC parameters nếu app behave weird. Thay đổi nhỏ có thể giảm memory consumption và prevent memory bloats.
Hy vọng bài này给 bạn idea tốt về customize Ruby GC module. Intro hơn: Introduction to Garbage Collection Part I và Part II.
Happy coding!