We're updating the issue view to help you get more done. 

Unused destructured local not cleared, causes memory leak

Description

Clojure currently doesn't clear unused locals. This is problematic as some form of destructuring can generate unused/unusable locals that the compiler cannot clear and thus can cause head retention:

1 2 3 4 5 6 ;; this works user=> (loop [xs (repeatedly 2 #(byte-array (quot (.maxMemory (Runtime/getRuntime)) 10)))] (when (seq xs) (recur (rest xs)))) nil ;; this doesn't user=> (loop [[x & xs] (repeatedly 200 #(byte-array (quot (.maxMemory (Runtime/getRuntime)) 10)))] (when (seq xs) (recur xs))) OutOfMemoryError Java heap space clojure.lang.Numbers.byte_array (Numbers.java:1252)

Here's a macroexpansion that exposes this issue:

1 2 3 4 5 6 7 8 9 10 user=> (macroexpand-all '(loop [[a & b] c] [a b])) (let* [G__21 c vec__22 G__21 a (clojure.core/nth vec__22 0 nil) b (clojure.core/nthnext vec__22 1)] (loop* [G__21 G__21] (let* [vec__23 G__21 a (clojure.core/nth vec__23 0 nil) b (clojure.core/nthnext vec__23 1)] [a b])

Cause: The first two bindings of a and b will hold onto the head of c since they are never used and not accessible from the loop body they cannot be cleared.

Approach: Track whether local bindings are used. After evaluating the binding expression, if the local binding is not used and can be cleared, then pop the result rather than storing it.

Patch: 0001-CLJ-1744-clear-unused-locals-v2.patch

Screened by: Alex Miller

Environment

None

Status

Assignee

Unassigned

Reporter

Nicola Mometto

Approval

Ok

Patch

Code

Fix versions

Priority

Critical