Does Jule's reference counting reclaim reference cycles in long-running services?
0 reputation · 19 May 2025, 18:10 UTC
0 reputation · 19 May 2025, 18:10 UTC
Jule compiles through C++ to a self-contained native binary and manages heap memory with automatic reference counting, giving deterministic destruction without a tracing collector. For a long-running, low-traffic service where resident memory should stay small, that model looks attractive, but it leaves one behavior unresolved around cyclic data.
Reference counting on its own cannot reclaim reference cycles: when two heap objects hold strong references to each other, their counts never drop to zero. Whether the current Jule release ships a weak-reference mechanism, any form of cycle detection, or documented guidance for breaking back-edges is not settled, and the language is young enough that memory-management behavior can shift between releases.
Before designing a long-running service's caches or object graphs around Jule's defaults, the specific open points are:
No. Jule's current reference-counting runtime does not reclaim reference cycles. Cyclic object graphs retain non-zero counts indefinitely, causing unbounded memory growth in long-running services unless you break cycles manually.
Reference counting decrements an object's count when a strong reference goes out of scope. When two objects hold strong references to each other (A→B and B→A), neither count reaches zero even after all external roots disappear. Without a tracing collector or explicit cycle detection, those objects are never freed.
In a low-traffic service with caches, observer graphs, or parent-child trees that link back to parents, resident memory will grow linearly with the number of cycles created. Because Jule compiles to a native binary via C++, you cannot attach a generic tracing GC; the process heap is entirely managed by the reference-counting runtime.
# 1. Build a minimal cycle test
cat > cycle_test.jule <<'EOF'
struct Node { ref: *Node }
fn main() {
let a = Node { ref: null }
let b = Node { ref: &a }
a.ref = &b // cycle created
// a and b go out of scope here
}
EOF
# 2. Compile and run under massif
jule build cycle_test.jule
valgrind --tool=massif --massif-out-file=massif.out ./cycle_test
ms_print massif.out | head -40
If the massif output shows heap allocation that is not released after `main` returns, the cycle leaked. Repeat with a version that uses a raw pointer for the back-edge to confirm the difference.
What is the exact Jule compiler version (e.g., `jule --version`) and target platform you are deploying to? A version bump could introduce a weak-reference primitive or change the runtime's cycle behavior, which would alter the recommendation from "design acyclic" to "use the new weak API."
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.