- 7 minutes read

There are all these wonderful tools for profiling Java programs. JVisualVM, JProfiler, Mission Control and the Flight recorder, jhat, just to name a few. Yet every once in a while, you can't use any of them, for one reason or another. Where do we go from here?

How come there's no profiler?

The situation is not quite as exotic as you may think. When I was asked recently to write programs in the middleware layer, the lack of tools was the one of the reasons why I denied. More precisely, I was asked to write Java programs running in the webMethods ESB. In theory, that's a great idea, but the lack of tools makes programming a pain. No debugger. No profiler.

In theory, you can debug such a program using remote debugging, but that requires many preconditions to be fulfilled. The administrator has to start the program with additional parameters, and they have to open the ports in the firewall. Sometimes that's possible in the development and test stages, but usually it's completely out of question in the production stage.

However, being a consultant, sometimes I'm asked to help our customer nonetheless. That was the situation I found myself today in. One of our customer's programs crashed due to an OutOfMemoryException. Thing is, the program to crash runs in a Talend server. I don't know many of the details, but I'm told we can't debug our programs. Actually, that's a situation familiar to me: more often than not, you aren't allowed to debug a program running in an ESB remotely. Especially, if the problem occurs only in the production environment.

Time for plan B.

The OutOfMemoryError indicates that our program creates a lot of objects without releasing them again. My first step to solve such a problem is to identify which of the many objects of the program causes the problem. That, in turn, boils down to simply counting the objects in memory and looking at the most frequent objects. Usually that's character arrays and strings, but usually one of the next top hit on the lists is the trouble-maker.

Write your profiler yourself

Googling for "How to count Java objects in memory" reveals ... well, many things, none of them useful. That's pretty surprising, because it's very simple to count the number of objects in main memory. Plus, it's a really useful technique if you're suffering from a memory leak.

Skipping the inevitable boiler plate code, it's as simple as that:

final VirtualMachine vm = ac.attach(connectArgs); final List allClasses = vm.allClasses(); final long[] instanceCounts = vm.instanceCounts(allClasses); for (int i = 0; i < allClasses.size(); i++) { System.out.println(allClasses.get(i).name() + ": " + instanceCounts[i]); }

There's a drawback: you need to configure your Java program properly. You have to add the tools.jar from the JDK. The annoying thing is that there isn't a standard way to do it using Maven. And you have to open a network port in the firewall. The API I'm presenting today works similarly to remote debugging. You have to deal with two separate programs: your payload program (that's the program already causing trouble) and a diagnosis program. Both communicate with each other via the network.

To run the program, you have to start the program under test in debug mode. In other words, you have to add some start parameters:

-Xdebug -Xrunjdwp:transport=dt_socket,address=3001,server=y,suspend=n

Using the Java Debug Interface (JDI) for introspection

The API I just described has been invented to enable a Java program to analyze another Java program. For instance, the debugger of Eclipse uses this API to debug your web application. However, Java programs can also use this API to analyze themselves. This way, you don't have to ask your operations department to drill a hole into the firewall. It suffices to allow the program to access the local debug port (i.e. 127.0.0.1:3001 in our example).

Alternative solutions

While using the JDI is the most efficient and most powerful approach, you're hardly ever allowed to use it. So I gathered two other approaches which may be useful, too.

When you're looking for "how to count Java objects in memory", many posts suggest to add a counter in the constructor. In my case, I'm not interested in how many objects have been created, but in how many objects have been created and haven't been removed by the garbage collector. So you have to add a finalizer, too. The most simple approach looks like so:

public class SimpleApproachBean { private volatile static int counter=0; public SimpleApproachBean() { counter++; } @Override protected void finalize() throws Throwable { counter--; super.finalize(); } }

Unfortunately, this simple approach is a bit cumbersome if you're interested in more than one or two classes. When you start investigating a performance issue, you hardly ever know in advance which class runs havoc. If you're using AspectJ, you can add code to every constructor of your project:

@Aspect @Component public class CommonLogAspect implements ILogAspect { private static int counter=0; @Before("execution(*.new(..))") public void countingConstructor() { counter++; } }

As far as I know, it's impossible to decorate a finalizer with AspectJ (or any other AOP tool I know of). After some investigation, I learned it's possible to register a Garbage collector callback using the ReferenceQueue class. The implementation is a bit cumbersome, but it works fine. It requires a ReferenceQueue to watch the object, a dedicated thread to watch the reference queue and a variable to store the ReferenceQueue. You know, the queue must be protected from being removed by the garbage collector prematurely. I've implemented a fairly generic solution:

public class CountingReferenceQueue extends WeakReference { public static ReferenceQueue queue = new ReferenceQueue(); private static HashMap queues = new HashMap<>(); static { new CleanupThread().start(); } public static long objectsCreated = 0; public static long activeObjects = 0; private long currentQueue; public CountingReferenceQueue(Object object) { super(object, queue); currentQueue=objectsCreated; queues.put(currentQueue, this); objectsCreated++; activeObjects++; } public void cleanUp() { activeObjects--; queues.remove(currentQueue); } } class CleanupThread extends Thread { public void run() { while (true) { CountingReferenceQueue ref; try { ref = (CountingReferenceQueue) CountingReferenceQueue.queue.remove(); ref.cleanUp(); } catch (InterruptedException e) { e.printStackTrace(); } } } }

After these preparations, we can keep track of the number of instances of an object by adding a call to the constructor:

public class PayloadBean { public PayloadBean() { new CountingReferenceQueue(this); } }

This, in turn, can be further simplified by some AspectJ magic, as outlined above.

Heap dumps

Analyzing a heap dump is a much more simple approach. Usually it's easy to convince the administrator to give you a heap dump. Either when the OutOfMemoryError occurs (using the JVM parameter -XX:+HeapDumpOnOutOfMemoryError), or manually using jmap:

jmap -dump:format=b,file=cheap.bin

(where is the process id of your running Java process).

The advantage of using jmap is that you can create and compare several heap dumps and watch how the memory allocation changes over time.

Wrapping it up

Analyzing a program without having access to your powerful debugger and/or your favorite profiler is always a chore. But fear not. It's still possible. It just a bit more work.


Dig deeper

StackOverflow on how to implement and use a ReferenceQueue

Using JPDA to write a debugger yourself

AspectJ constructor annotation

Oracle's documentation on the debug APIs of Java


Comments