troubleshoot: use JFR to solve memory leaks

Introduction

Although java has automatic GC, there will be memory leaks. Of course memory leaks in java are different from leaks in C++.

All allocated memory objects in C++ need to be freed manually by the programmer. But this process is not needed in java, everything is done automatically by GC. So is there no memory leak in java?

To answer this question we first need to define what a memory leak is. If sometimes the objects we no longer use cannot be released by the GC, then it can be said that a memory leak occurs.

The main reason for memory leaks is that the life cycle of objects in java is long or short. If a long-lived object references a short-lived object, it may cause a de facto memory leak.

An example of a memory leak

Let's take an example of a memory leak, first define a large object:

public class KeyObject {
    List<String> list = new ArrayList<>(200);
}

and then use it:

public class TestMemoryLeak {

    public static HashSet<Object> hashSet= new HashSet();

    public static void main(String[] args) throws InterruptedException {
        boolean flag= true;
        while(flag){
            KeyObject keyObject= new KeyObject();
            hashSet.add(keyObject);
            keyObject=null;
            Thread.sleep(1);
        }
        System.out.println(hashSet.remove(new KeyObject()));
    }
}

In this example, we put the new KeyObject object into the HashSet.
Then set keyObject to null.

But because the class variable hashSet still retains a reference to keyObject, the keyObject object will not be recycled.

Note that in the last line we added a hashSet.remove code to use the class variable hashSet.
Why do you want to do this? This is done to prevent the JIT from optimizing the code and thus affecting our analysis of memory leaks.

Using JFR and JMC to analyze memory leaks

Flight Recorder(JFR) is mainly used to record JVM events, and we can analyze memory leaks from these events.

JFR can be turned on with the following command:

java -XX:StartFlightRecording

Of course, we can also use the java artifact jcmd to open JFR:

jcmd pid JFR.dump filename=recording.jfr path-to-gc-roots=true

Here we use JMC to graphically analyze the above example.

Open JMC, find our test program, open the flight recorder.

You can see that our object allocated 4MB of memory during the flight recorder, and then see that the overall memory usage is steadily increasing.

When do we know there will be a memory leak? The simplest is definitely OutOfMemoryErrors, but some very subtle memory leaks will cause memory usage to rise slowly, and we need to conduct detailed analysis at this time.

Through analysis, we see a steady increase in memory usage, which is actually quite suspicious.

Next, we analyze it through the OldObjectSample event of the JVM.

OldObjectSample

OldObjectSample samples objects with a long life cycle, and we can examine these objects to check for potential memory leaks.

Here we pay attention to the Old Object Sample event in the event browser, we can see the details of the event in the lower left.

Or you can use the jfr command to parse the output directly for events of interest:

jfr print --events OldObjectSample flight_recording_1401comflydeanTestMemoryLeak89268.jfr   > /tmp/jfrevent.log

Let's look at a specific output Sample:

jdk.OldObjectSample {
  startTime = 19:53:25.607
  allocationTime = 19:50:51.924
  objectAge = 2 m 34 s
  lastKnownHeapUsage = 3.5 MB
  object =  [
    java.lang.Object[200]
  ]
  arrayElements = 200
  root = N/A
  eventThread = "main" (javaThreadId = 1)
  stackTrace = [
    java.util.ArrayList.<init>(int) line: 156
    com.flydean.KeyObject.<init>() line: 11
    com.flydean.TestMemoryLeak.main(String[]) line: 17
  ]
}

lastKnownHeapUsage is the usage size of the heap, and we can see from the log that this value has been increasing.

allocationTime represents the time the object was allocated.

startTime represents the time when the object was dump ed.

object represents the allocated object.

stackTrace represents the stack information that this object is allocated to.

Note that if you need to display stackTrace information, you need to enable the -XX:StartFlightRecording:settings=profile option.

From the above log, we can analyze that the 17th line in the main method, that is, KeyObject keyObject= new KeyObject(); is constantly creating new objects.

This allows us to conduct a deeper analysis and finally find the cause of the memory leak.

Summarize

This article introduces how to analyze memory leaks through the use of JFR and JMC. Hope everyone likes it.

The author of this article: those things about the flydean program

Link to this article: http://www.flydean.com/jvm-diagnostic-memory-leak/

Source of this article: flydean's blog

Welcome to pay attention to my official account: those things about the program, more exciting things are waiting for you!

Tags: Java jvm

Posted by paxman356 on Mon, 23 May 2022 11:12:28 +0300