Why do we need Java profilers?
In this tutorial we will explain why do we need Java profilers, and we will see visualvm
(very popular Java profiler) in action.
Why do we need profilers?
Java profiler provides a detailed information about Java applications while they are running on a Java Virtual Machine (JVM). We can use profilers to monitor Java application. Few features of Java profilers:
- Monitor Java threads
- Monitor application memory consumption
- Display application configuration and runtime environment
- Monitor communication between Java application and SQL database (you can find great article on JDBC profiling here)
Why do we choose visualvm
and not some other profiler tool?
Because the visualvm
is the closest to the “official Java profiler tool”. This tool was bundled directly with JDK 6, JDK 7, and JDK 8. Only after JDK 8 (when developers of JDK decided that all tools that are not core of the JDK should be separated from JDK) tool was evicted from the JDK and became a standalone tool. It was part of the JDK at some point and that is why I consider it an official Java profiler. Another advantage of the visualvm
is that it is open source and free.
How to install visualvm
?
Download visualvm
from here. Extract zip file somewhere and just run the file named visualvm.exe
which is located inside the bin
folder. You will see the home screen of the visualvm
.
Monitoring an application
After you started visualvm
you should start your application. When you start your Java application, you will notice the name of your application in the upper left side of the visualvm
(in my case, under Local
tab you can see the name of my application com.example.demo.DemoApplication
):
Double click on the name of your application and we can start monitoring what’s going on in our app!
This is the starting screen of your application:
In the Overview
tab you can see some overview of your application (Java version, Java home, JVM flags, PID, etc.). Also, you can see JVM arguments
and System properties
. This post is a beginner friendly, but it is about visualvm
so I will skip explaining basic stuff such as what is system propery, JVM argument, heap dump, thread dump, etc.
In the next Monitor tab, you can visually see four diagrams:
- CPU consumption
- Memory usage
- Data about number of classes loaded
- Data about threads
Also, in the upper right corner there are two buttons Perform GC and Heap Dump which are self explanatory. Heap dump is extremly helpful. If you perform a heap dump, snapshot of your memory is created and you can analyze your memory consumption later as well. This way you can find memory problems such as memory leaks.
Analyzing heap dump
Let’s try to analyze heap dump. For the sake of example I wrote this code:
@Component
@Slf4j
public class MainTry implements CommandLineRunner {
List<Person> persons;
@Override
public void run(String... args) throws Exception {
Thread.sleep(10000);
PersonService personService = new PersonService();
persons = personService.createPersons();
personService.filterPersons(persons);
log.info("Inserting and filtering is done.");
}
}
public class PersonService {
public List<Person> createPersons() {
List<Person> persons = new ArrayList<>();
for(int i = 0 ; i < 50000000; i++) {
persons.add(new Person("John",(i % 10) + 1));
}
return persons;
}
public void filterPersons(List<Person> persons) {
persons.removeIf(person -> person.getAge() > 5);
}
}
Interesting note: If you make persons
list a local variable instead of the field of the class, you will see different results in the heap dump. It doesn’t have anything to do with visualvm
. Can you figure out why the result is different?
Let’s get back to the code and explain it. It’s a simple job that firstly wait ten seconds and then creates 50 milion persons and add them to the list. After persons are in the list, we filter only the persons that are older than 5.
Start you java application and wait for the Job is done
message. Then, click on the Heap Dump button and let’s switch to the heap dump tab. Here is how heap dump tab looks like:
There are 25 milion persons in your heap! That is because we filtered out half of the persons and those persons are garbage collected. On the right side under Classes by Size of Instances
you can see that 67% of the heap size is populated with Person
objects. If you click on the Summary
(marked red on the picture) and select Objects
you can expand every object inside your heap and see the content and size of that object! Another great thing is that you can see where is your object referenced at!
Analyzing single object
In the picture we can see one object of class Person
. On the right side we can see the size of an object. Also, we can see value of the firstName
and age
fields and under references
we can see that object is referenced inside an ArrayList
persons
inside MainTry
class. The last thing regarding heap dump is that if you choose Threads insted od the summary(marked red on the picture) you can see thread stack for every thread! There are plenty of other options inside the Heap Dump. Feel free to explore.
Analyzing CPU
The next question is: “which operation takes more time(inserting or filtering)”. You can find an answer to that question inside either Sampler or Profiler tabs. Great explanation what is the difference between those two tabs can be found here. In short, Profiler is more precise but it slows down an application significantly. Sampler is doing samples so it does not slow down an application but it is way less precise. Let’s try to compare inserting and filtering using Profiler first.
Start your application. As soon as you see your application in the visualvm
, click on it and go to the Profiler
tab. Click on the CPU button. It is very important to start profiling(click on the CPU button) before inserting and filtering starts because only in that scenario visualvm
will actually profile those methods. It was different in the Heap Dump scenario simply because application add objects to the memory and those objects are not going out of the memory! Here is the result of the profiling:
You can see that filtering is way slower then the inserting! You can try the same measurement using Sampler. Of course as you can guess inside Sampler and Profiler tabs you can also analyze a memory as well. It’s just that I find it easier to create a heap dump and then analyze it.
Conclusion
And that would be it. You learned how to profile a Java application using visualvm
. You know how to see JVM parameters, system properties, how to analyze heap and thread dump(thread dump button is inside the Threads tab, just click on it), how to analyze memory and CPU. The last thing that I want to mention is that there is a possibility to profile an application on the remote server and not only local application. In the upper left corner just instead of the local go to the Remote click Add Remote Host and just app a port and the IP of the remote app and everything would work the same!