In general, it is recommended that a Java application’s JVM garbage collection settings be left alone. The JVM in most cases does a good job of selecting proper garbage collection algorithms, and tweaking this configuration turns off the automatic cleaning ergonomics, usually resulting in decreased or sub-optimal garbage collection performance.
A Java Message Oriented Middleware solution like ActiveMQ is always the exception to this rule. Message Oriented Middleware tends to create many small, short-lived objects inside the JVM. This means that most objects will reside in the JVM heap’s “New” or “Eden” area of memory, and that the potential for memory fragmentation increases significantly. The specific behavior of Message Oriented Middleware lends itself to different object patterns and behavior than seen in other Java applications.
Although it is possible to achieve better performance using the traditional JVM garbage collection by reducing the size of the Eden memory area, tests performed by both the ActiveMQ community and Rogue Wave Software have consistently shown that the newer Garbage-First Garbage Collector (G1GC) is the best choice for ActiveMQ. G1GC lends itself to shorter individual garbage collection cycles, reducing the duration of “stop-the-world” collections, and therefore reducing the probability of disruption of message delivery.
Goals for optimizing garbage collection in ActiveMQ
First, reducing the duration of a garbage collection pause can mean the difference between a buggy and unstable message delivery system and a solid, long-running one. With so many messages coming in and out of the broker, especially in a high-frequency messaging environment, even short disruptions can cause message backlog. The point of ActiveMQ is to be a stable and fast mechanism for moving data around an infrastructure, and it should never be the bottleneck to this work.
Second, the memory usage of the broker itself should be consistent, avoiding fragmentation and therefore reducing the probability of an out-of-memory condition, so that a broker can run for months or even years at a time without interruption.
How G1GC helps
G1GC helps to accomplish these goals by partitioning the JVM heap into segments, or “buckets” as they are often called, of like objects. Partitioning the heap this way allows for several efficiency optimizations:
• The garbage collector can correlate similar objects into the same area of memory, and choose either parallel or concurrent mark-sweep garbage collection based on current object behavior
• Segments in memory with the most object waste can be cleaned up first, allowing even an incomplete garbage collection run to clean up as many disposable objects as possible
• Heap memory fragmentation can be isolated and compacted in segments rather than compacting the entire heap
• An exact number for the garbage collection pause can be specified, ensuring the shortest possible span of broker downtime during a parallel garbage collection
Turning on and tuning G1GC
G1GC is very easy to turn on. The “-XX:+UseG1GC” parameter can be added to the ACTIVEMQ_OPTS or ACTIVEMQ_OPTS_MEMORY variables in the /bin/activemq launch script. A number of additional tuning parameters are available to you which will allow explicit control over the exact amount of time that the collector spends cleaning during each cycle, the size of various areas in the heap, and the number of individual object buckets that are created. For more information on tuning G1GC, check out Oracle’s guide on the subject.
Once you’ve turned on G1GC, a number of JMX mBeans are available to you which can help you monitor garbage collection behavior to ensure that it is performing as desired. These mBeans can be found under java.lang.GarbageCollector and java.lang.Memory in the JMX tree:
In the picture, you can see the attributes specific to the G1GC garbage collector. Important mBeans here include:
|LastGCInfo||A Composite Data structure with values including:GcThreadCount – Total number of threads used in the GC
Duration – time in Milliseconds that the garbage collection took
startTime and endTime – Start and end times of last GC
memoryUsageBeforeGc and memoryUsageAfterGc – The amount of Heap Space in use before and after the GC completed
|CollectionCount||The total number of GCs performed since the broker started|
|CollectionTime||The total amount of time spent garbage collecting|
These are broken out into G1 Young Generation and G1 Old Generation categories, so that you can look at statistics from garbage collection in both the New and Old areas of memory.
In addition to using JMX, you can also use the Visual GC plugin for JVisualVM, which comes with your JDK. JVisualVM can be launched from the “/bin” directory of your JDK installation, and provides a nice visual of memory management within the JVM, as well as a set of tools such as a live thread analyzer, a memory and CPU sampler, and a lightweight application profiler.
From the Tools -> Plugins menu, you can install the Visual GC plugin:
Which can be used to provide a visual overview of garbage collection behavior in the JVM, including the total duration of GC Time, the total number of collections, and the amount of memory in each area of the JVM Heap:
The best choice for ActiveMQ
The G1GC garbage collection algorithm has consistently proven to be the best choice for ActiveMQ. By categorizing objects into buckets and cleaning up as much waste as possible, G1GC can adapt itself well to a message environment with both high and low message volume, and with messages of varying sizes. Because G1GC allows explicit control over its behavior, you can tune it to work around the precise nature of your messaging system.
Instead of spending time wrestling with the standard JVM garbage collection ergonomics, why not just turn on G1GC and let it do the heavy lifting for you?