Skip to content

Clojure Guide — GraalVM: Compiling Clojure to Native

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Clojure Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

GraalVM native image technology compiles Clojure applications ahead-of-time into standalone native executables that start instantly, use less memory, and require no JVM installation.

What You'll Learn

  • What GraalVM native images are
  • Setting up GraalVM for Clojure
  • Compilation considerations and limitations
  • Performance characteristics
  • Deployment strategies

Why It Matters

Native images make Clojure viable for serverless functions, CLI tools, and containers where startup time and memory matter. Durga Antivirus Pro uses native images for its command-line scanner.

Real-World Use

Serverless Clojure functions on AWS Lambda benefit from sub-100ms startup. CLI tools like clj-kondo use GraalVM for native distribution without requiring Java.

flowchart LR
    A["GraalVM"] --> B["Setup"]
    B --> C["Compilation"]
    C --> D["Limitations"]
    D --> E["Deployment"]
    A:::current --> B
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Installing GraalVM

# Install GraalVM with SDKMAN
sdk install java 21.0.2-graal

# Or download from graalvm.org
export GRAALVM_HOME=/path/to/graalvm
export PATH=$GRAALVM_HOME/bin:$PATH

# Install native-image tool
gu install native-image

Project Setup

;; project.clj
(defproject my-native-app "0.1.0"
  :dependencies [[org.clojure/clojure "1.11.1"]]
  :main my-native-app.core
  :aot :all)

;; deps.edn with Lein for AOT
{:deps {org.clojure/clojure {:mvn/version "1.11.1"}}}

Simple Native App

(ns my-native-app.core
  (:gen-class))

(defn -main [& args]
  (println "Hello from native Clojure!")
  (println "Args:" args))

;; Compile
;; lein uberjar
;; native-image -jar target/my-native-app-0.1.0-standalone.jar

Static Initialization

;; GraalVM builds a heap snapshot at build time
;; Use delay for runtime-initialized values
(def config
  (delay
    (slurp "config.edn")))

;; Or mark for runtime initialization
;; In native-image command:
;; --initialize-at-run-time=my-app.core

GraalVM with Reflection

;; GraalVM needs to know about reflection
;; Option 1: JSON reflection config

;; reflect-config.json
[{"name":"java.lang.Class",
  "methods":[{"name":"forName","parameterTypes":["java.lang.String"]}]}]

;; native-image -H:ReflectionConfigurationFiles=reflect-config.json

;; Option 2: @RegisterReflection annotation
;; (requires Java interop)

Resource Access

;; Resources must be explicitly included
;; native-image -H:IncludeResources="config/.*\.edn$"

;; Access at runtime
(defn load-resource [path]
  (slurp (clojure.java.io/resource path)))

CLJS with GraalVM

# Compile ClojureScript to JS, then use GraalVM's JavaScript runtime
# Or use GraalPolyglot for polyglot native images

clj -M -m cljs.main -co "{:target :node}" -c src/my_app/core.cljs
native-image --language:js -jar my-app.jar

Performance Characteristics

;; Startup time comparison
(time (defn -main [] (println "Hello")))
;; JVM: ~2-3 seconds
;; Native: ~0.01 seconds

;; Memory comparison
;; JVM: ~100MB baseline
;; Native: ~10MB baseline

;; Peak performance
;; JVM: Faster after warmup (JIT)
;; Native: Consistent from start

Common Mistakes

1. Dynamic class loading

GraalVM needs to know all classes at build time. Avoid Class.forName and dynamic class loading.

2. Missing reflection config

If your app uses reflection, provide a reflection configuration file or GraalVM will throw errors at runtime.

3. Not handling resources

Files accessed via clojure.java.io/resource must be explicitly included at build time.

4. Serialization issues

Java serialization requires special configuration. Use JSON (JSONista) or EDN instead.

5. Unsupported Java features

Dynamic proxies, JMX, and some security managers are not supported in native images.

Practice Questions

1. What does GraalVM native image do? It compiles JVM bytecode ahead-of-time into a native executable with a compressed heap snapshot, eliminating JVM startup and warmup time.

2. Why use native images for Clojure? Faster startup (milliseconds vs seconds), lower memory (10MB vs 100MB+), and no JVM dependency for deployment.

3. What are the limitations of native images? Reflection, dynamic class loading, and some Java features require explicit configuration or are unsupported.

Challenge: Compile a Clojure CLI tool with GraalVM and measure the startup time difference.

FAQ

{{< faq question="Can I use all Clojure libraries with GraalVM?" >}} Most work, but libraries using reflection, dynamic Code Generation, or classloader tricks need configuration. Clojure itself is well-supported. {{< /faq >}}

{{< faq question="How long does compilation take?" >}} Native image compilation is slow (1-10 minutes depending on app size). This is a build-time cost, not runtime. {{< /faq >}}

{{< faq question="Is native image production-ready?" >}} Yes. GraalVM native images are used in production by Oracle, Twitter, and many others. The technology is mature and well-supported. {{< /faq >}}

{{< faq question="Can I debug native images?" >}} Yes, with limited support. You can use GDB for native debugging, generate a heap dump, or use the GraalVM Inspector for deeper analysis. {{< /faq >}}

{{< faq question="Does native image support all JDK features?" >}} No. Some features like JMX, dynamic proxies, and SecurityManager have limited or no support. Check the GraalVM compatibility guide. {{< /faq >}}

Mini Project

Create and compile a native Clojure CLI app:

(ns greeter.core
  (:gen-class))

(defn -main [& args]
  (let [name (or (first args) "World")]
    (println (str "Hello, " name "!"))
    (println "This is a GraalVM native image.")))

;; Build:
;; lein uberjar
;; native-image -jar target/greeter-0.1.0-standalone.jar -H:Name=greeter
;; ./greeter Alice

What's Next

Now that you understand GraalVM, explore Clojure performance optimization techniques.

Topic Description Link
Clojure Performance Optimization techniques {{< ref "29-performance" >}}
Clojure Community Ecosystem and resources {{< ref "30-community" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro