Optimizing the internals of the MBean Server
Summer is of course the time when people take their vacation,
and nowhere more so than here in France. You could be forgiven
for thinking that the entire country grinds to a halt between
the traditional vacation boundaries of the 14th of July ( href="http://en.wikipedia.org/wiki/Bastille_Day">Bastille
Day) and the 15th of August ( href="http://en.wikipedia.org/wiki/Assumption_of_Mary">Feast of
the Assumption, a public holiday,
don't ask). It seems like
three quarters of high-street shops are closed for business.
More to the point, three quarters of my co-workers are also on
vacation, so, if I'm not, then the amount of time I spend in
discussion and co-ordination is drastically reduced.
This summer I had a period of about two weeks like that where I
could just lower my head and code. And that was a good thing,
because there was something that really needed to be done.
The MBean Server is the core of the JMX API, and its
implementation in the JDK includes code that dates from the very
earliest release of that API, and indeed its proprietary
predecessors. That means it's had six years or more to
accumulate bug-fixes, interesting ideas that didn't quite work
out, new features that were grafted on as best we could, and so
last straw for me was the addition of href="http://download.java.net/jdk6/doc/api/javax/management/MXBean.html">
MXBeans to the JMX API. MXBeans were initially
implemented as a standalone package that used only the public
JMX API. When the MXBean feature was added to href="http://mustang.dev.java.net">
Mustang, we just did the minimal adaptation work to make
it work. It was still operating as a standalone package,
taking no advantage of the implementation classes in the rest
of the JMX API. The idea was to get the feature in place
quickly, then revisit it later to clean up.
If you're like me, you're extremely sceptical about things that
"we'll clean up later". Somehow, if it works, it never does get
cleaned up. There's always something more urgent to do. I
could see this coming a mile off for MXBeans.
This was bad for a number of reasons. MXBeans and Standard
MBeans have a great deal in common. In fact you could consider
that Standard MBeans are just MXBeans where the href="http://download.java.net/jdk6/doc/api/javax/management/MXBean.html#mapping-rules">
type mapping is the identity. So the fact that they
shared almost no code meant that there was a lot of
functionality implemented twice. Bug-fixes applied to one
wouldn't automatically apply to the other. New features, such
annotations for descriptor contents would have to be
implemented separately for each one. And optimizations in one
wouldn't appear in the other.
This was exactly the situation we were in during the French
shutdown. Standard MBeans had lots of caching logic so that the same
MBeanInfo instance could be shared between all MBeans of
the same type, and only computed for the first of them.
MXBeans didn't have that, but they did cache
Method objects so they didn't have to be retrieved again
invoke on the MBean.
So, out with the hatchet. Refactor the logic for Standard
MBeans and MXBeans so that they are just two concrete subclasses
of an abstract MBean class. Put the caching of MBeanInfo
objects and Methods into this class. Use the
Visitor Pattern so the same introspection logic can
construct MBeanInfo, method cache, and proxy data.
In this sort of environment, you have to pay lots
of attention to
WeakReferences so that all the cached information goes
away when it's no longer used. It's really easy to set things
up so that if you register an MBean from some class loader and
then unregister it again, the class loader object is still
referenced and can never be garbage collected.
While I had the hatchet out, I changed the way MBeans are
handled so that every MBean is ultimately a
DynamicMBean. Standard MBeans and MXBeans get wrapped in
a DynamicMBean. The previous logic had separate handlers for
Dynamic MBeans and Standard MBeans, and that tended to produce
gratuitous inconsistencies between the two.
The intent of all these changes is to simplify the code, so
future evolution will be easier; to reduce per-MBean memory
usage; and to speed up MBean operations. The first of these is
hard to measure but the measurements I've made on the other two
encouraging. These changes will appear in build 53 of
Mustang, which should be available on href="http://mustang.dev.java.net/">java.net on about the
23rd of September.