Clojure
Clojure, reflection, and performance/memory issues
Right now, I’m working in a game project in Clojure. I don’t really know how it will turn out, but for now I’m just trying to learn a better way of making games.
While working in this project, I found out that my game was consuming a lot of memory. I’m using play-clj library, and I know that it creates a lot of small objects for each render cycle, so that was my first guess.
So, I plugged in a VisualVM in my running game to understand what was happening. In the beginning, nothing seemed to make sense: the heap grew, then was released, the correct and normal cycle of any Java application. Then, I tried a memory profiling and a memory dump. Then, things became interesting.
There were a lot of float[] objects popping up, as I would expect – play-clj uses floats to position elements on the screen, and all the time I found myself trying to coerce doubles to floats. But there was something even stranger there was consuming a lot of memory: instances of java.lang.Method.
For those who don’t know, Clojure interoperability with Java relies on reflection when it can’t resolve a type. To resolve a type means that Clojure can be certain that, at run time, that a specific identifier will be a specific type. So, for the following code:
(ns example.core) (defn sum-abs [a b] (Math/abs (/ a (float b)))) (defn only-abs [a] (Math/abs a))
The first method call will use reflection because it knows that the result of a sum will always be a float. The second one has no idea if it will be called with a number or not, so it relies on reflection. It may seem strange, as we’re calling Math/abs
, but remember that in Java we can have different methods with the same name, differing only on type signature.
So, to resolve the type, we’ll need type hints. But first, we can test if our code is using reflection using lein check
.
(more…)