The problem

Clojure's support for dynamic development incurs runtime costs that may be undesirable in certain production environments.  These include:

Additionally, the indirection involved using a Var, especially via getting a reference to a Var via Var.intern(String, String) makes Clojure-generated bytecode hard to analyze via programs like ProGuard.

Startup time

Starting a Clojure program takes a significant amount of time, most of which is taken up by the Clojure runtime bootstrapping itself, i.e. loading and initialising namespaces and vars (see Why is Clojure bootstrapping so slow?).  For long-running applications, this is probably not an issue.  However, this is a problem in other scenarios:

Android

Android applications should ideally load about as fast as Java or Scala applications.  Currently, on a high-end telephone, a minimal Clojure program will take about one second longer, which is perceptible.  In some cases, having a Java splash screen thrown up while Clojure bootstraps may be an acceptable solution, but that doesn't work for all programs.

Command line programs/utilities

Depending on the type of utility, Clojure's start time can dominate the time used to do the actual work.  Current workarounds include using persistent JVMs, i.e. nailgun, or using ClojureScript with Node.js.

Google App Engine (GAE)

GAE imposes a strict sixty second time limit for responding to a request.  If the application hasn't warmed up, Clojure's startup can take up a significant portion of that time, resulting in the initial request timing out.

Var.getRawRoot overhead

Generally speaking, each time a Var is used within a function invocation, it's root binding must be retrieved.  While this is a very simple operation, it does require reading a volatile variable, which may impede optimisation.  In some programs, this overhead is measurable.  Currently, this best workaround for this is using a macro or definline, but this results in code that is harder to read and write.

Heap use

Clojure 1.5.1 uses over seventeen megabytes of heap size just starting a basic REPL.  Much of this heap use results from Clojure simply loading up a lot of Vars which may or may not be used in a given program. For servers with gigabytes of RAM, this may just be noise.  However, in more constrained environments, e.g. Android, this can be more of an issue.

Manual tree-shaking via commenting out portions of clojure.core has shown significant reductions in heap use.  Unfortunately, namespaces are currently atomic and cannot be easily broken up.

Deployment size

Clojure 1.5.1's JAR takes up about 3.5 megabytes.  This isn't an issue for a lot of environments, but this can be an issue for mobile applications or Clojure as a library.

Mitigation strategies

A number of mitigation strategies have been proposed to resolve one or more of the above issues:

  1. Static compilation
  2. Lazy loading of Vars
  3. Don't load the user namespace by default
  4. Eliminate the compiler

Static compilation

Lazy loading of Vars

Don't load the user namespace by default

Eliminate the compiler

Other things to think about

Development plan

Related Hammock topics