Garbage Collector in LACE.jl
Overview
LACE.jl comes with a custom garbage collector for managing C objects. This collector provides a bridge between Julia and C memory management:
C-side Memory Management:
- C objects are allocated and managed by LACE's custom garbage collector
- The collector implements a mark-and-compact strategy for C memory:
- Objects are marked for retention using roots
- During collection, objects are moved to new locations
- Unused memory is freed
Julia-side Memory Management:
- Julia code holds
Ptr{Cvoid}
references to C objects - These pointers need to be updated when the collector moves objects
- The
LACE.ACTIVE_POINTERS
dictionary in Julia tracks these pointers - After collection, pointers are updated to point to new object locations
- Julia code holds
How It Works
Basic rules
Always mark the heap before creating new objects:
LACE.jl_mark_default_heap()
Register objects you want to keep:
# For system pointers LACE.register_sys_pointer(:sys_ptr, sys_ptr, Cint(number_of_equations)) # For polynomial pointers LACE.register_pol_pointer(:pol_ptr, pol_ptr)
These functions handle both:
- Registering in Julia's
LACE.ACTIVE_POINTERS
- Setting roots in the C garbage collector
- Registering in Julia's
Unregister objects if you change your mind (before collection):
# For system pointers LACE.unregister_sys_pointer(:sys_ptr, sys_ptr, Cint(number_of_equations)) # For polynomial pointers LACE.unregister_pol_pointer(:pol_ptr, pol_ptr)
Similarly, these functions handle both:
- Unregistering in Julia's
LACE.ACTIVE_POINTERS
- Unsetting roots in the C garbage collector
- Unregistering in Julia's
Clean up the heap periodically:
LACE.collect_default_heap_and_update_pointers() LACE.jl_unmark_default_heap()
or, if you are sure that there are no objects that need to be kept:
LACE.collect_default_heap() LACE.jl_unmark_default_heap()
Verify object status when needed:
LACE.print_tab_cont_default_heap() # Check what objects are tracked LACE.infos_default_heap() # Check heap status
Examples
1. Managing Multiple Objects
When you need to create multiple objects but only keep some of them:
using LACE, MPFI, DynamicPolynomials
@polyvar x
f1 = BigInterval(3.5) * x^4 + BigInterval(8) * x^2 + BigInterval(1, 2)
f2 = BigInterval(4.5) * x^4 + BigInterval(8) * x^3
# Mark the heap before creating any objects
LACE.jl_mark_default_heap()
# Create objects you do not want to keep (don't register them)
sys_ptr1 = LACE.sys_get_C_ptr([f1])
# Ptr{Nothing} @0x00000001704d0038
# Create objects you want to keep
sys_ptr2 = LACE.sys_get_C_ptr([f2])
# Ptr{Nothing} @0x00000001704d02a0
LACE.register_sys_pointer(:sys_ptr2, sys_ptr2, Cint(1))
# Verify that the objects are registered
LACE.print_tab_cont_default_heap()
LACE.ACTIVE_POINTERS
# Dict{Symbol, Ptr{Nothing}} with 1 entry:
# :sys_ptr2 => Ptr{Nothing} @0x00000001704d02a0
# Clean up the heap
LACE.collect_default_heap_and_update_pointers() # Clean up
LACE.jl_unmark_default_heap()
# Now only sys_ptr2 is still valid and it is updated to the new location
sys_ptr2
# Ptr{Nothing} @0x00000001704d0038
# No more objects are registered
LACE.ACTIVE_POINTERS
2. Unregistering and Cleaning Up Objects
When you need to unregister an object that was previously registered:
using LACE, MPFI, DynamicPolynomials
@polyvar x
f = BigInterval(3.5) * x^4 + BigInterval(8) * x^2 + BigInterval(1, 2)
LACE.jl_mark_default_heap()
# Create and register an object
sys_ptr = LACE.sys_get_C_ptr([f])
LACE.register_sys_pointer(:sys_ptr, sys_ptr, Cint(1))
# Later, when you want to unregister it
LACE.unregister_sys_pointer(:sys_ptr, sys_ptr, Cint(1))
# The object is now unregistered and will be collected
# You can verify this by checking tab_cont
LACE.print_tab_cont_default_heap() # Should show 0 objects
# Clean up the heap
LACE.collect_default_heap()
LACE.jl_unmark_default_heap()