Memory Management: ARC, ORC and Destructors
31.1 Memory Management Options (--mm:)
orc (default in Nim 2) ARC + cycle collector: deterministic, works for cyclic data
Pure reference counting with move semantics. Cycles leak unless broken manually. Best for hard real-time and embedded.
Legacy deferred reference counting GC (default in Nim 1.x) Legacy tracing GC Boehm conservative GC Go's GC (for interop) No memory management (manual)
31.2 How ARC/ORC Work
The compiler injects destructor calls (=destroy), copies (=copy), and moves (=sink) based on static analysis of variable lifetimes.
Reference counts are not atomic by default. Threads share data via Isolated, channels, or --threads:on with atomic refcounts... see Module 33.
Last-read optimization: the last use of a variable becomes a move instead of a copy.
ORC adds a trial-deletion cycle collector that only examines potentially cyclic types. Mark types {.acyclic.} to skip it.
--expandArc:procName shows the injected memory operations:
nim c --mm:orc --expandArc:main app.nim31.3 Lifetime-Tracking Hooks
Disable copying: proc =copy(dst: var Buffer, src: Buffer) {.error.} makes the type move-only (like Rust's non-Copy or C++ unique_ptr).
type
Buffer = object
data: ptr UncheckedArray[byte]
len: int
proc `=destroy`(b: Buffer) = # Nim 2 signature (non-var)
if b.data != nil: dealloc(b.data)
proc `=wasMoved`(b: var Buffer) =
b.data = nil
proc `=copy`(dst: var Buffer, src: Buffer) =
if dst.data == src.data: return
`=destroy`(dst)
`=wasMoved`(dst)
dst.len = src.len
if src.data != nil:
dst.data = cast[ptr UncheckedArray[byte]](alloc(src.len))
copyMem(dst.data, src.data, src.len)
proc `=sink`(dst: var Buffer, src: Buffer) = # move: steal resources
`=destroy`(dst)
dst.data = src.data
dst.len = src.len
proc `=dup`(src: Buffer): Buffer = ... # Nim 2: optimized copy for construction
proc `=trace`(b: var Buffer; env: pointer) = ... # ORC: trace refs inside custom containers
proc newBuffer(n: int): Buffer =
Buffer(data: cast[ptr UncheckedArray[byte]](alloc0(n)), len: n)31.4 RAII Patterns
Files, sockets, locks, and GPU handles are released automatically by destructors when the owner goes out of scope.
defer for ad-hoc cleanup.
31.5 Breaking Cycles Under ARC
The {.cursor.} pragma for non-owning back-references (parent pointers):
type Node = ref object
children: seq[Node]
parent {.cursor.}: Node # doesn't increment the refcount31.6 Memory Debugging
-d:useMalloc (use the system malloc, which helps with Valgrind/ASan), --passC:-fsanitize=address --passL:-fsanitize=address, getOccupiedMem(), -d:nimArcDebug, GC_fullCollect() (ORC cycle collection), GC_disableOrc.