JMH ships with a number of built-in profiler options that have grown in number over time. The profiler system is also pluggable, allowing for "after-market" profiler implementations to be added on the fly.
Many of these profilers, most often the ones that stay in the realm of Java, will work across platforms and architectures and do so right out of the box. Others may be targeted at a specific OS, though there is a good chance a similar profiler for other OS's may exist where possible. A couple of very valuable profilers also require additional setup and environment to either work fully or at all.
This guide will cover setting up both the async-profiler and the Perfasm profiler. Currently, we roughtly cover two Linux family trees, but much of the information can be extrapalated or help piont in the right direction for other systems.
You JMH with the lprof
argument, it will make an attempt to only list the profilers that it detects will work in your particular environment.
You should do this first to see where you stand.
In our case, we will start with very minimal Arch and Ubuntu clean installations, and so we already know there is no chance that async-profiler or Perfasm
are going to run.
In fact, first we have to install a few project build requirements before thinking too much about JMH profiler support.
Here we give async-profiler a try on Arch anyway and observe the failure indicating that we need to obtain the async-profiler library and put it in the correct location at a minimum.
Running JMH with args: FuzzyQuery -prof async:output=flamegraph Profilers failed to initialize, exiting. Unable to load async-profiler. Ensure asyncProfiler library is on LD_LIBRARY_PATH (Linux) DYLD_LIBRARY_PATH (Mac OS), or -Djava.library.path. Alternatively, point to explicit library location with: '-prof async:libPath={path}' no asyncProfiler in java.library.path: [/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib]
wget -c https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.5/async-profiler-2.5-linux-x64.tar.gz -O - | tar -xz sudo mkdir -p /usr/java/packages/lib sudo cp async-profiler-2.5-linux-x64/build/* /usr/java/packages/lib
That should work out better, but there is still an issue that will prevent a successful profiling run. async-profiler relies on Linux's perf, and in any recent Linux kernel, perf is restricted from doing its job without some configuration loosening.
The following changes will persist across restsarts, and that is likely how you should leave things.
sudo sysctl -w kernel.kptr_restrict=0
sudo sysctl -w kernel.perf_event_paranoid=1
No we should see a bit of success.
But you will also find the following if you look closely at the logs. We do not want the debug symbols stripped from Java for the best experience. And it also turns out that if we want to use async-profilers alloc option to sample and create flamegraphs for heap usage, the debug symbols are required.
[WARN] Install JVM debug symbols to improve profile accuracy
On the Arch side we will rebuild the Java 11 package, but turn off the option that strips debug symbols. Often, large OS package and Java repositories originated in SVN and can be a bit a of a bear to wrestle with git about for just a fraction of the repository, we do so GitHub API workaround efficiency.
sudo pacman -Syu dkms base-devel linux-headers dkms git vi jq --needed --noconfirm curl -sL "https://api.github.com/repos/archlinux/svntogit-packages/contents/java11-openjdk/repos/extra-x86_64" | jq -r '.[] | .download_url' | xargs -n1 wget
Now we need to change that option in PKGBUILD. Choose your favorite editor. (nano, vim, emacs, ne, nvim, tilde etc)
vi PKGBUILD
We insert a single option line:
arch=('x86_64')
url='https://openjdk.java.net/'
license=('custom')
+ options=('debug' '!strip')
makedepends=('java-environment>=10' 'java-environment<12' 'cpio' 'unzip' 'zip' 'libelf' 'libcups' 'libx11' 'libxrender' 'libxtst' 'libxt' 'libxext' 'libxrandr' 'alsa-lib' 'pandoc'
And then we build. (-s: --syncdeps -i: --install -f: --force
)
When that is done, if everything went well, we should be able to succesfully run asyncprofiler in alloc mode to generate a flamegreaph based on memory rather than cpu.
Perfasm hsdis for output assembly
wget -c https://aur.archlinux.org/cgit/aur.git/snapshot/java11-openjdk-hsdis.tar.gz -O - | tar -xz
[root@2c99f04f894a jmh]# cd java11-openjdk-hsdis/
[root@2c99f04f894a java11-openjdk-hsdis]# ls
binutils-compat.patch binutils.patch PKGBUILD
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk/ sudo apt update sudo apt -y upgrade sudo apt -y install openjdk-11-jdk git wget jq
curl -sL "https://api.github.com/repos/openjdk/jdk11/contents/src/utils/hsdis" | jq -r '.[] | .download_url' | xargs -n1 wget
# Newer versions of binutils don't appear to compile, must use 2.28 for JDK 11
wget http://ftp.heanet.ie/mirrors/ftp.gnu.org/gnu/binutils/binutils-2.28.tar.gz tar xzvf binutils-2.28.tar.gz make BINUTILS=binutils-2.28 ARCH=amd64