For some reason, I was under the impression that jvisualvm(1) was just a rebranding of jconsole(1). It turns out that it isn't, and that – unlike jconsole(1) – it's not a waste of space.

It's not very clever about dividing up space between its various graphs, but other than that it looks pretty nice. I thought I wanted a quick heap profile to see why one of my applications ate so much memory that my machine always felt swappy afterwards. When jvisualvm(1) showed that there wasn't much heap retention going on, I was almost ready to dismiss it as broken, but thought I'd play about and see what else it could do. Turning to the "threads" tab, I saw my actual problem: I had several hundred idle threads.

It turned out I'd once again forgotten that "an unused ExecutorService should be shut down to allow reclamation of its resources".

I had code like this (only without the call to shutdown):

  ExecutorService service = Executors.newFixedThreadPool(threadCount);
  for (Input input : inputs) {
    service.execute(new Job(input));

Every time this ran, which was pretty frequently, I'd leak another 8 or so threads, that I'd never use again.

I should write myself some kind of "do this batch of jobs on n threads and then kill the threads" wrapper.

Anyway, although I was disappointed not to find any heap retention in any of my applications, I was quite pleased with jvisualvm(1) for finding a performance problem, even though it would have been as easy to spot with control-\ — I think that having all the tools together in one place makes me a bit more likely to bother to poke about now and again.

If you have trouble installing plugins like VisualGC, try quitting and restarting. I found that – contrary to the error message – they had actually installed okay, and appeared in the UI when I restarted. They even seem to work.