lambdaclojurescopejvm

How does JVM Clojure store captured environments of closures under the hood?


When I define a function that captures some of its variables in the outer scope, such as below, how does Clojure (on a JVM host) actually store the captured environment?

(let [small-datastructure (calculate-small-thing)
      large-datastructure (calculate-large-thing)]
  (if *feeling-lucky*
    (fn capturing-closure [] (do-other-thing small-datastructure))
    (use-data large-datastructure))

In particular, the function capturing-closure only references the small-datastructure, but not the large-datastructure. When I return capturing-closure from this code, will the large-datastructure still be referenced by capturing-closure, or will it be released to garbage collection?

I'm open to both direct answers or suggestions for how to accurately measure this.


Solution

  • how does Clojure (on a JVM host) actually store the captured environment?

    Every function becomes a class, every captured thing becomes a member field.

    When I return capturing-closure from this code, will the large-datastructure still be referenced by capturing-closure, or will it be released to garbage collection?

    The latter.

    suggestions for how to accurately measure this.

    You can compile the code and inspect the generated .class files in whatever way you prefer. Or you can use https://github.com/clojure-goes-fast/clj-java-decompiler.