Monitoring Jvm Performance: Tips For Optimal Java Virtual Machine Efficiency

how to monitor jvm performance

Java Virtual Machine (JVM) is an integral part of any Java application and its performance directly impacts the application's performance. To ensure the smooth running of a Java Virtual Machine, it is important to monitor its performance and tweak it regularly to ensure peak performance. JVM performance monitoring involves tracking key performance indicators like heap memory, garbage collection, thread count, and application uptime in real time. This allows for the identification of potential issues and helps maintain business continuity. JVM monitoring tools provide insights into how the JVM is functioning, including CPU usage, memory usage, and activities of functions like Garbage Collection. These tools can also provide proactive alerts to help application operations and DevOps teams understand and troubleshoot any bottlenecks in the JVM that may be affecting application performance.

Characteristics Values
Memory Heap memory, non-heap memory, memory leaks, memory pools, memory allocation and release
Garbage Collection Frequency, time taken, number of objects garbage collected, type of GC algorithm
Threads Number of busy and free threads, time spent in CPU, thread leaks, deadlocks, blocked threads
Uptime Application uptime, server uptime
Performance Response time, transaction response time, CPU capacity/usage, application performance, server performance
Tools JConsole, VisualVM, Site24x7, ManageEngine Applications Manager, eG Enterprise, AppOptics, Datadog APM, Sematext Monitoring, Dynatrace

shundigital

Heap memory management

Heap memory is the memory space that holds all the objects in your application. When there are more users accessing your application, performing a greater number of transactions, more and more objects containing request-specific data are created in heap memory.

Once the requests are served, these objects should be cleared or unreferenced. If they’re not, the objects will keep occupying the heap space, leaving no space for new objects. This leads to memory management issues, and your application may crash due to OutOfMemory (OOM) errors.

Heap memory issues can be split into two categories: scalability issues and improper coding practices.

Say your Java application has 2GB of heap space and you suddenly notice a spike in the number of users. This obviously increases the number of objects created in your JVM to serve these requests. This, in turn, consumes more heap memory, leading to application unavailability for certain users. This is a scalability issue.

When there is the usual traffic to your application but your application is performing poorly, one of the reasons could be that there are too many referenced objects in storage, occupying more heap. This type of issue needs to be taken care of while developing your application, as it relates to improper coding practices.

This is where monitoring heap memory comes in handy.

In the image above, we can see a sudden spike in the heap memory usage at a particular time. You can correlate the application response time with the number of requests and check if the spike is because of increased requests or an improper code base.

The heap is created when the JVM starts up and may increase or decrease in size while the application runs. The size of the heap can be specified using the –Xms VM option. The heap can be of fixed size or variable size, depending on the garbage collection strategy. The maximum heap size can be set using the –Xmx option. By default, the maximum heap size is set to 64 MB.

The heap is physically divided into two parts (or generations): the nursery (or young space/young generation) and the old space (or old generation).

The nursery is a part of the heap reserved for the allocation of new objects. When the nursery becomes full, garbage is collected by running a special young collection, where all the objects that have lived long enough in the nursery are promoted (moved) to the old space, thus freeing up the nursery for more object allocation. This garbage collection is called Minor GC. The nursery is divided into three parts: Eden Memory and two Survivor Memory spaces.

Important points about the nursery space:

  • Most of the newly created objects are located in the Eden Memory space.
  • When Eden space is filled with objects, Minor GC is performed, and all the survivor objects are moved to one of the survivor spaces.
  • Minor GC also checks the survivor objects and moves them to the other survivor space. So, at a time, one of the survivor spaces is always empty.
  • Objects that have survived many cycles of GC are moved to the old generation memory space. Usually, this is done by setting a threshold for the age of the nursery objects before they become eligible to be promoted to the old generation.

When the old generation becomes full, garbage is collected there, and the process is called old collection. Old generation memory contains the objects that are long-lived and survived after many rounds of Minor GC. Usually, garbage collection is performed in Old generation memory when it’s full. Old generation garbage collection is called Major GC and usually takes longer. The reasoning behind a nursery is that most objects are temporary and short-lived. A young collection is designed to be swift at finding newly allocated objects that are still alive and moving them away from the nursery. Typically, a young collection frees a given amount of memory much faster than an old collection or a garbage collection of a single-generational heap (a heap without a nursery).

The heap stores the actual objects. It is instantiated during the virtual machine startup. This memory is allocated for all class instances and arrays. The JVM provides the user control to initialize or vary the size of the heap as per the requirement. When a new keyword is used, the object is assigned space in the heap, but the reference to the same exists on the stack.

There are four types of references: strong, weak, soft, and phantom references. The difference among the types of references is that the objects on the heap they refer to are eligible for garbage collecting under different criteria.

Strong reference: Any object with a strong reference attached to it is not eligible for garbage collection. We can create a strong reference by using the following statement:

Object obj = new Object();

Weak Reference: It does not survive after the next garbage collection process. If we are unsure when the data will be requested again, in this condition, we can create a weak reference to it. In case the garbage collector processes, it destroys the object. When we try to retrieve that object again, we get a null value. It is defined in java.lang.ref.WeakReference class. We can create a weak reference by using the following statement:

WeakReference obj = new WeakReference(new Object());

Soft Reference: It is collected when the application is running low on memory. The garbage collector does not collect the softly reachable objects. All soft-referenced objects are collected before it throws an OutOfMemoryError. We can create a soft reference by using the following statement:

SoftReference obj = new SoftReference(new Object());

Phantom Reference: It is available in java.lang.ref package. It is defined in java.lang.ref.PhantomReference class. The object, which has only a phantom reference pointing to them, can be collected whenever the garbage collector wants to collect. We can create a phantom reference by using the following statement:

PhantomReference obj = new PhantomReference(new Object(), ReferenceQueue);

shundigital

Garbage collection

There are several tools available to monitor GC:

  • Jstat: A monitoring tool in HotSpot JVM that provides information on GC operation, class loader operation, and Just-in-Time compiler operation.
  • Jconsole: A graphical user interface (GUI) tool provided by the Java SE platform that displays information about the performance and resource consumption of applications running on the Java platform.
  • Jvisualvm: A GUI profiling/monitoring tool provided by Oracle JDK that offers basic information about GC and Heap.
  • Visual GC: A plugin for jvisualvm that provides more detailed information about GC and Heap in real time.
  • HPJMeter: A performance monitoring tool developed by HP that can be used to analyse -verbosegc output results and provide GUI analysis of GC performance data.
  • JMX Technology: Built-in management utilities in the Java VM that enable monitoring and management using JMX-compliant tools such as jconsole.

By monitoring GC, you can gain insights into your application's memory usage and choose the most suitable GC algorithm for your needs.

shundigital

Thread monitoring

To effectively monitor threads, several tools and techniques are available. Java provides default monitoring tools such as JConsole and VisualVM, which display live data. However, these tools lack alerting mechanisms. More advanced tools, like Site24x7, offer additional features such as historical data comparison, transaction-level metrics, and alerting options. These tools provide insights into thread usage and help identify potential bottlenecks.

Additionally, the JMX technology, integrated with the Java VM, offers built-in management utilities. Platform MXBeans, a set of MXBeans provided with the Java SE platform, enable monitoring and management of the Java VM and other JRE components. These beans can be visualised and interacted with using JConsole, a graphical user interface that complies with the JMX specification.

Furthermore, tools like NewRelic, jstack, jvmtop, and threadcpu are also recommended for monitoring thread execution and identifying CPU usage abnormalities. These tools provide detailed insights into thread activity and help troubleshoot performance issues.

By utilising these thread monitoring tools and techniques, you can ensure that your application has sufficient threads to handle user requests efficiently, preventing delays and improving overall JVM performance.

shundigital

Memory leaks

There are several ways to identify and address memory leaks:

  • Performance degradation: If an application's performance gradually deteriorates over time, it may be due to a memory leak.
  • OutOfMemoryError: If an application runs out of memory and terminates with an OutOfMemoryError, it is a clear sign of a memory leak.
  • Spontaneous crashes: Unexpected and unusual application crashes may also indicate memory leaks.
  • Verbose garbage collection: Enabling verbose garbage collection can help identify memory leaks by providing detailed traces of the garbage collection process.
  • Profiling tools: Using Java profilers, such as VisualVM or JConsole, allows you to monitor memory usage and identify potential memory leaks.
  • Reference objects: Java provides built-in reference objects in the java.lang.ref package that can help manage memory and avoid leaks.
  • Proper coding practices: Memory leaks can often be prevented by following proper coding practices, such as minimising the use of static variables, properly closing resources, and correctly implementing equals() and hashCode() methods.
  • ThreadLocals: Memory leaks can occur when ThreadLocals are not properly removed, leading to objects being retained even after the thread is no longer alive.
  • Finalizers: Overriding the finalize() method can introduce memory leaks if not implemented optimally, as objects will be queued for finalization instead of immediate garbage collection.

shundigital

CPU usage

There are several ways to monitor CPU usage. One method is to use the ThreadMXBean.getThreadCPUTime() method from the Thread MBean. This allows a Java Virtual Machine implementation to support measuring the CPU time for the current thread, for any thread, or for no threads.

Another way to monitor CPU usage is through the use of basic Linux commands. For example, the 'top' command can be used to display all CPUs and their utilisation statistics. The 'ps -p [pid] -o %cpu,%mem,cmd' command can be used to display the CPU and memory usage of a specific Java application method, along with the command that initiated the process.

Additionally, Java Profilers can be used to monitor CPU usage. These tools observe Java bytecode constructs and processes, as well as all system commands and processor usage. This allows for an in-depth analysis of CPU usage and can help identify any issues causing high CPU load.

By monitoring CPU usage and keeping the number of active threads in check, you can ensure optimal performance of your JVM-based application.

Frequently asked questions

JVM stands for Java Virtual Machine. It is the core of the Java application architecture and is the runtime engine of the Java Platform. It interprets and translates Java bytecode into operations on the host platform.

JVM performance affects Java application performance. By monitoring JVM, you can identify and troubleshoot performance issues, such as bottlenecks and memory leaks, that may impact your Java applications.

Some key JVM metrics to monitor include heap memory usage, garbage collection, thread activities, CPU usage, and memory leaks.

There are several tools available for monitoring JVM performance, such as AppOptics, eG Enterprise, ManageEngine Applications Manager, Site24x7 APM, Datadog APM, Sematext Monitoring, and Dynatrace. These tools offer features like infrastructure and application monitoring, performance tracking, and root cause analysis.

Written by
Reviewed by
Share this post
Print
Did this article help you?

Leave a comment