clj-async-profiler is an embedded high-precision performance profiler for
Clojure. Embedded means there is no software to install on your system, instead
you add the profiler to your application as a dependency. From there, you can
operate the profiler either programmatically or via a web UI. During profiling,
clj-async-profiler has very low overhead, so it is suitable for usage even in
highly loaded production scenarios.
clj-async-profiler presents the profiling results as an interactive
flamegraph. You can navigate the
flamegraph, query it, change parameters and adapt the results for easier
Example flamegraph. Click to open the interactive version.
To collect the profiling data, clj-async-profiler utilizes
async-profiler which is
a low overhead sampling profiler for Java. Current version of async-profiler
that is used by clj-async-profiler is
Comprehensive usage guide and in-depth documentation are available at Clojure
Goes Fast knowledge
clj-async-profiler is only supported on GNU/Linux and MacOS. On Linux, you need
to allow async-profiler to use kernel profiling data by setting these two
sudo sysctl -w kernel.perf_event_paranoid=1
sudo sysctl -w kernel.kptr_restrict=0
com.clojure-goes-fast/clj-async-profiler to your dependencies. This
is the latest version:
JDK11+: you must start your application with JVM option
-Djdk.attach.allowAttachSelf, otherwise the profiling agent will not be able
to dynamically attach to the running process. For Leiningen, add
:jvm-opts ["-Djdk.attach.allowAttachSelf"] to
project.clj. For tools.deps, add the same
deps.edn or write
in your REPL command.
clj-async-profiler.core exposes an all-in-one facade for generating profiling
flame graphs. The most common usage scenario looks like this:
(require '[clj-async-profiler.core :as prof])
;; Profile the following expression:
(prof/profile (dotimes [i 10000] (reduce + (range i))))
;; The resulting flamegraph will be stored in /tmp/clj-async-profiler/results/
;; You can view the HTML file directly from there or start a local web UI:
(prof/serve-ui 8080) ; Serve on port 8080
You can also start and stop the profiler manually with
Each profiling command accepts a map of options. See docstrings for each command
for the list of supported options, or Functions and
Option map for each profiling command can have a
:pid value. If it is
provided, an external JVM process with this PID will be sampled, otherwise the
current process is targeted.
For a detailed description of clj-async-profiler's more advanced features, see
the documentation pages:
Also check out this video from London Clojurians meetup:
By default, clj-async-profiler writes its output files to
/tmp/clj-async-profiler/. You can change it to a custom directory (e.g., if
you run clj-async-profiler in an environment where
/tmp is not sufficiently
large) by setting Java property
clojure -J-Dclj-async-profiler.output-dir=./data ...
It is highly recommended to use
-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints JVM flags. Without those flags the profiler will still
work correctly but results might be less accurate. Without these options, there
is a high chance that simple inlined methods will not appear in the profile.
When agent is attached at runtime, CompiledMethodLoad JVMTI event enables debug
info, but only for methods compiled after the event is turned on.
If you see stackframes like
/usr/lib/.../libjvm.so, it means that you have to
install JDK debug symbols. E.g., on Ubuntu that would be the package
clj-async-profiler ships with the precompiled native libraries that
- Linux: x64, x64 with musl libc (Alpine), aarch64 (arm64)
- MacOS: x64/aarch64 (universal binary)
To use clj-async-profiler on other supported
you should do the following:
for the desired platform.
Put the resulting
libasyncProfiler.so in a place accessible by your JVM
process (and which also allows code execution from).
Execute from Clojure:
tools.deps is used as a build
tool. Regular build tasks are inside build.clj and invoked as
clojure -T:build test,
clojure -T:build jar, etc.
When starting the REPL, you should add
dev alias on the list so that
virgil is loaded. Then, to compile Java
classes in the REPL, do:
user> ((requiring-resolve 'virgil/compile-java) ["src"])
async-profiler is distributed under Apache-2.0.
See APACHE_PUBLIC_LICENSE file. The location of the original
Copyright 2017-2024 Andrei Pangin
clj-async-profiler is distributed under the Eclipse Public License.
Copyright 2017-2024 Alexander Yakushev