This is a ultra-short post on JVM debugging tools. Did you get an OutOfMemoryError and have no idea how to proceed? This post is for you.

An example for memory analysis

I’d like to start with a dummy application as example. We will make it run and analyse its memory heap. With the tools I will introduce you can analyse many other indicators, but the procedure to get to that point hast the same initial steps. Once in the tool you can explore by yourself.

This application simply will create a huge amount of UUID instances and keep them in memory for a while. If we analyse it correctly, we should see them somewhere in our memory heap. So let’s get started.

Put in Main.scala the following:

package com.mauritania

import java.util.UUID.randomUUID

object Main {
  val Nb = 1500000

   // my unique id string dummy class
  case class Myuids(ui: String)

  def main(args: Array[String]): Unit = {

    val u = (1 to Nb).
      map(_ => Myuids(randomUUID().toString))

    java.lang.Thread.sleep(3600 * 1000)

    u.foreach(println)

  }

}

Clearly we’re allocating many Myuids instances.

Launch it as follows (using scalac 2.11.7 here):

scalac Main.scala
scala com.mauritania.Main

Generating heap dumps

There are sevaral ways to generate heap dumps (.hprof files) before we can analyse them:

  1. Using jmap JDK’s tool as follows:
 jmap -dump:format=b,file=heap.hprof <pid-of-running-jvm>
  1. Connecting to JVM via mat tool
  2. Connecting to JVM via jmc tool
  3. Connecting to JVM via jvisualvm tool
  4. Requesting the JVM to dump the heap on OOM using the following JVM settings:
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heap.dump/

Analysing heap dumps

Eclipse Memory Analyser (mat)

The easiest way of analysing memory heaps is using mat tool.

  1. Download the standalone Eclipse Memory Analyser (EMA, or mat).
  2. Open mat

Mat

  1. Get the memory dump of the just started JVM, it must be an hprof file.
  2. Choose the Component report

Component

  1. Point to the package you’re monitoring (in a regex), for instance com\.mauritania\..*
  2. Now you must see a pie chart.

Pie

  1. Click on Top Consumers, see their size in MB.

Top

The chart shows how Myuids instances are occupying the heap, just as expected.

Note: normally you can also open .hprof files with jhat.

Java Mission Control (jmc)

Let’s now use jmc.

  1. Add the following settings to your JVM:
-XX:+StartAttachListener
-XX:+UnlockCommercialFeatures
-XX:+FlightRecorder
-XX:FlightRecorderOptions=
  defaultrecording=true,
  dumponexit=true,
  dumponexitpath=/tmp/jfr/,
  repository=/tmp/jfr/,
  disk=true
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/heap.dump/
  1. Upon an OutOfMemoryError the HeapDump* settings will make the JVM create a heap dump under /tmp/heap.dump/java_pidXXXXX.hprof
  2. Upon exit the FlightRecorder* settings will make the JVM create a report under /tmp/jfr/hotspot-pid-XXXXX-id-XXXXXXXXXXX.jfr (which does not have memory heap exhaustive information, but more general indicators)

Regarding our own application, it is enough to launch our application as follows, as all I wanted is to connect to the JVM without dump files:

java -cp .:<path-to>/scala/lib/scala-library.jar \
  -XX:+UnlockCommercialFeatures \
  -XX:+FlightRecorder \
  com.mauritania.Main
  1. You can open the .hprof heap dump as seen in the sections above.
  2. To open the .jfr you need to use Java Mission Control (tool from jdk: jmc)
  3. Instead of opening an existent .jfr you can make jmc connect to an existent JVM, as follows:

a. Create a new connection

Connect

b. Set up the recording settings

Save Save

c. Interestingly you can request jmc to record certain events from the JVM, like file open event. This can be useful if you consider your application is slow because it has too much IO.

Event

d. Start recording

Start Rec

e. See general performance indicators, their evolution in time too

Stats

f. Go deeper into memory consumption under package org.mauritania

Stats

Extra notes

Use the following JVM settings to get garbage collection activities logs.

-XX:+PrintGCDetails 
-XX:+PrintGCTimeStamps