Perhaps the largest benefit of JRuby being on the JVM is the excellent tool ecosystem. There’s an enormous collections of debuggers, profilers, and general monitoring tools available for JVM that work great with JRuby. Even better, a surprising number of these tools are built into each JVM.
One of these tool sets is the java.lang.management
package. Here, you’ll find a number of JMX beans for monitoring the status and health
of the JVM. Some of the information presented by these beans is standard, like lists
of memory pools
(heaps) or the number of available processors
on the current system. But each JVM may also expose additional information.
On OpenJDK, starting with 6u25, the built-in ThreadMXBean
exposes an additional operation: getThreadAllocatedBytes
. How can JRubyists take
advantage of it?
Monitoring Thread Allocation
Of course, via JRuby’s Java integration, we can easily call any of the management beans’
operations, and getThreadAllocatedBytes
is no different.
We start by loading the ‘java’ and ‘jruby’ libraries, to access Java classes and some normally-hidden JRuby features.
require 'java'
require 'jruby'
We get access to the ThreadMXBean
via the ManagementFactory
class.
thread_bean = java.lang.management.ManagementFactory.thread_mx_bean
Now, we will create a thread that endlessly creates a new string, and get references
to that thread’s and the main thread’s native java.lang.Thread
object.
t1 = Thread.new do
a = nil
loop do
a = 'foo'
end
end
t1_thread = JRuby.reference(t1).native_thread
main = Thread.current
main_thread = JRuby.reference(main).native_thread
Now that we’ve got a thread busily allocating data, we set up a loop that prints out
both threads’ allocated bytes once every second. The getThreadAllocatedBytes
method takes an array of thread IDs and returns an array of byte counts, both as
long[].
loop do
sleep 1
t1_alloc = thread_bean.get_thread_allocated_bytes([t1_thread.id].to_java(:long))[0]
main_alloc = thread_bean.get_thread_allocated_bytes([main_thread.id].to_java(:long))[0]
puts "main allocated: #{main_alloc}"
puts "t1 allocated: #{t1_alloc}"
end
(Note the bit of Java integration array-munging; par for the course going from Ruby’s heterogeneous Array to Java’s homogeneous arrays.)
And that’s it! Here’s the output on my system for five iterations of the loop:
main allocated: 11343752
t1 allocated: 378806608
main allocated: 11359632
t1 allocated: 767226768
main allocated: 11361624
t1 allocated: 1156928944
main allocated: 11363616
t1 allocated: 1547160976
main allocated: 11365608
t1 allocated: 1930237360
I’ve gisted the full script here: Monitoring Thread Allocation.
Your Turn
This is just one of many fun (and useful!) ways you can monitor the JVM using JRuby.
Poke around in ManagementFactory
and see what else you can find!