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?

MWB
  • 11,740
  • 6
  • 46
  • 91
  • My "stress-test" of another Lisp [here](https://stackoverflow.com/questions/65432918/) -- two functions call each other in a mutually recursive fashion and get replaced as they do it. – MWB Dec 25 '20 at 03:18

2 Answers2

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.

MWB
  • 11,740
  • 6
  • 46
  • 91
Alan Thompson
  • 29,276
  • 6
  • 41
  • 48
  • 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.... – MWB Dec 21 '20 at 20:20
  • Clojure function is just an object of the AFn class (https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/AFn.java). If a reference to this object (function) is lost, it's GCed. – generateme Dec 21 '20 at 21:31
  • 2
    @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. – amalloy Dec 21 '20 at 23:14
  • @Alan Thompson, your ns declaration is missing a closing paren. – Juraj Martinka Dec 22 '20 at 07:32
  • My "stress-test" of another Lisp [here](https://stackoverflow.com/questions/65432918/) -- two functions call each other in a mutually recursive fashion and get replaced as they do it. – MWB Dec 25 '20 at 03:17
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.

AlBlue
  • 23,254
  • 14
  • 71
  • 91