The JVM provides a number of management and monitoring interfaces that you, as a JRuby user, can use to keep an eye on or profile your application. There are interfaces for monitoring threads, monitoring memory usage, monitoring loaded JVM classes, and even for monitoring JRuby internals. The full set of built-in monitors are documented in the JDK's java.lang.management docs.
In this article, we'll explore some of the memory monitoring beans and how you can use them to analyze the memory characteristics of your application. I'll be using the default Java 6 version for OS X 10.6.3, which is based on Hotspot, the JVM inside OpenJDK.
We start by requiring 'java' and importing the ManagementFactory from the JDK.
Now we have access to the four primary beans associated with memory management on the JVM: GarbageCollector, MemoryManager, Memory, and MemoryPool MXBeans. We'll grab them all first.
Let's start with the simplest of these beans, the Memory bean.
The Memory bean provides the most basic information: current heap usage, current non-heap usage, and the count of objects waiting to be finalized. There's also methods to force a GC run and to enable verbose GC logging. Let's check current stats, run some code with verbose logging on, and force a GC run:
Here's the output so far:
(The GarbageCollector beans provide information about all the garbage collectors in use on a given JVM. They allow you to query collection counts and total collection time.
Below, we'll do some work that should trigger at least a couple GC runs, and then print out stats.
And here's the output for my JVM:
You may notice that each garbage collector has several memory pools associated with it. Let's look at the memory pools now.
Memory pools represent areas of memory used to isolate objects with differing characteristics. For example, some JVMs will list a "Par Eden Space", which represents the memory pool containing the newest and youngest objects. Some JVMs list a "Perm Gen" which will contain objects only collected during full GC runs.
Let's iterate over all memory pools and get information on each:
Here's the listing of memory pools for me:
Note that the "usage" values are actually instances of MemoryUsage, which provides four different values for the pool:
init: the amount of memory the JVM initially reserved from the OS
used: the amount of memory currently used
committed: the amount of memory actually available
max: the maximum memory that will ever be available
These will change over time as objects are collected and the heap grows to meet application needs. Let's show the current usage for the Par Eden Space, as an example:
The output for "Par Eden Space":
Memory pools also allow you to set usage thresholds. When a threshold is crossed, the JVM will increment a counter. This allows you to monitor whether or not your application is regularly exceeding a particular threshold, so you can adjust memory settings or work to reduce your application's demands.
Here's what that might look like for the "Par Survivor Space," the "old" pool. We'll monitor the collection usage threshold, or the amount of space in use after the most recent GC run. Some JVMs will support this or the general usage threshold, but neither are required to be supported.
We'll set a limit that's 5MB larger than the last collection usage and then run another loop that keeps memory alive a bit longer.
And the result:
Crossing the threshold here means that the minimum usage of the pool after a collection went over this size that many times. In this example, we create a 10,000 element array 1000 times and then throw it away; but while it's alive, it gets promoted into this "Survivor" space. So we repeatedly create it, promote it, and release it, resulting in the threshold being crossed several times.
The final bean we haven't covered, the MemoryManager bean, is usually just the superclass for other beans like GarbageCollector, so there's not much more to say about them. On my system, the list of MemoryManager beans is the same as the list of GarbageCollector beans.
Hopefully this walkthrough has whetted your appetite for the JVM's management and monitoring interfaces. In future posts, we'll cover how you can use the other builtin monitors, how you can track JRuby's internal monitors, and even how you can register your own monitors for any application. As always, leave questions and comments!
CHECK OUT OUR CURATED COLLECTIONS
Look through our specially curated posts to get focused, in-depth information on a single topic.