3

If you replace functions at runtime frequently, the way this poster wanted to do for the purposes of , will Clojure/JVM GC old code?

1
  • My "stress-test" of another Lisp here -- two functions call each other in a mutually recursive fashion and get replaced as they do it. Commented Dec 25, 2020 at 3:18

2 Answers 2

5

When in doubt, ask the computer! I wrote a quick test:

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test))
  
(defn create-fn
  [n]
  (let [fname   (symbol (format "add-%d" n))
        body    (vector '[x] (list '+ n 'x))
        fn-form (apply list 'clojure.core/defn fname body)
        fn-ref  (eval fn-form)]
    fn-ref))

(defn create-and-call-fn
  [n]
  (let [fn-ref (create-fn n)
        fn-result (fn-ref 3)]
    (when-not (= (+ 3 n) fn-result)
      (throw (ex-info "bad result" (vals->map n fn-result))))))


(dotest
  (let [f5 (create-fn 5)
        f5-result (f5 3)]
    (is= 8 f5-result))
  (dotimes [i 99999]
    (when (zero? (mod i 100)) (spyx i))
    (create-and-call-fn i)))

and up to 100K function creations later, it still works. YMMV!


The above is based on this template project.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks! Your function probably doesn't get JITed because it's (a) simple (b) gets called only once. I'll try to experiment with it later....
Clojure function is just an object of the AFn class (github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/…). If a reference to this object (function) is lost, it's GCed.
@generateme But when you eval a function, you generate a new class, not just a new object. Whether that class gets cleaned up is less obvious than whether some random lambda does.
@Alan Thompson, your ns declaration is missing a closing paren.
My "stress-test" of another Lisp here -- two functions call each other in a mutually recursive fashion and get replaced as they do it.
1

The answer is yes, the generated code should get collected at some point at the future. When it does is not specified; it may happen late, or never.

The other thing to watch out for is (particularly when using code from another eval) is whether you have circular references in the code that gets generated which prevent them from being unloaded. Typically when a ClassLoader gets released, all of its classes get released as well -- and some interpreted/JITd languages on top of the JVM use this to allow the code to be recycled. When the classes are unloaded, any compiled code is also jettisoned -- but if you have a reference to an instance of that class of that classloader (say, in the ThreadLocalStorage) then it will pin the instance, then the class, then the classloader, so it's possible that they will still leak.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.