Want this course?
Course: JVM Fundamentals for Clojure
We look at several tools for querying the JVM so that we can monitor it.
All right, let's talk about monitoring a JVM. JVMs are complex software that needs to be, you gotta keep an eye on it, make sure it's doing what you think it is, because it's not always obvious what it's gonna do. So there are a lot of tools to do that. It has a little API that these tools can connect up to, so that they can do some cool stuff. So the big monster, visual GUI version of the monitoring is called VisualVM. So I got this Clojure program running here. It's using Quill. And it just rotates this stupid shape in a circle, just over and over. And that way I know it's still running. There's stuff going on, stuff to monitor.
So I'm going to switch to the VisualVM, which is a program you can download. Let's go look at the website. So visualvm.github.io. I guess they're too cheap for a domain name. But anyway, you just download it and run it. It's free, it's available. So here it is. This is what it looks like. You can see on the left, you got all the JVMs running. Unfortunately, they just have a generic name. VisualVM is itself, obviously. And then these other ones, what are they? Well, here's the trick that I use. Cause I often have several things running at once. I open it, double click on it, it opens it, and I check out two things.
One, is this the working directory So this is the right directory. I'll show you the wrong directory. And this other thing is some stuff, when I write the Clojure Gazette, I have some tools to help me do that. Okay, so that's not the right one, I'm going to close it. Now look at this other thing. The class path includes Leiningen. So this is actually Leiningen's own JVM. Like when you write Lein-repo, it boots up Leiningen separate from your program.
So I'm going to look for the other one. Okay, this has the Gazette, that's not right. This one is game-dev, and look, it doesn't have that Leiningen line, in the class path. So this is the right one. Okay, so I'm going to close the other ones, so I don't get confused. Now, those are the tabs of the open JVMs. You can go into the monitor, and you see a bunch of stuff about it, right? Now, this is obviously, you gotta be on the same machine. If you want to do remote, there's other stuff for that. Okay, but on the same machine, it's socket connection, so it's really fast.
I don't know if it's a Unix socket, or if it's still TCP socket. I'm not sure. But look, we can see how big the heap is getting. It's growing, so there's some kind of usage of memory that isn't being collected yet. CPU usage is nice and low. Number of threads that's nice. Number of classes, it's not loading anything new, so it's just constant. Not sure what Metaspace is. But you can see you have a nice view of what's going on. See it's constantly going up, the used heap. But watch, I'm going to do a Perform GC. Boom. It garbage collected all that stuff. And starts growing again. So that's obviously normal garbage in a Clojure program. I'm iterating through a seek or something, and it's making a lot of little objects. Or something is iterating through a seek, it might not be me. It might not be my code.
This is another thing, it's showing you all the different threads, and what their status is. That's pretty cool. If you're doing a lot of parallel work, this is great. This is where it starts to get cool. There's this sampler and profiler. So if I want to profile the CPU, I'm going to click that. And what it's doing is it's pulling the JVM for the stack, Like, "What is on the stack right now?" And it just does that a ton of times. Like, "What's on the stack? "What's on the stack? "What's on the stack?" And you let it run for a little bit. And so it's statistically sampling what is being run, what functions are being run. And it's going to take a little bit of time, because it's gotta generate ... Maybe it's sampling.
There we go. Sorry, I was on the wrong tab. So if you do the sampler, the CPU, it's sampling the stack, like I was saying. You can see everything that's being run, and what's on the top of the stack, and the things that called it. And it's calculating, it's guessing how much time is being spent in each method based on how often it is at the top of the stack. So you can see the self time is the amount of time that that function is on the top of the stack, how long does it take to run. This is multiplied. It might be run a billion times, and it takes a microsecond, or it might be it's runs three times, and each time it takes five seconds, right? So it's a multiplication there. But you can see where your most important code is. Something that's not run very much is not going to show up on here. This is stuff that's a lot of time is spent in it. And you can see that it's some kind of event loop, or something from processing, which Quill is based on, is built on top of. And then we can see that, uh-oh, we're doing a lot of reflection, we're getting the methods of something.
So that's bad. But, you know what, it's only 196 milliseconds in this whole time that we've been running, because we're still sampling. Not a big deal, maybe you want to get rid of that. I'm just going through these things, seeing what I see. Because I'm not going to go into optimization right now. I just want to show that you can do some cool stuff. So I'm going to stop it.
So that's something that I'll use a lot when I'm monitoring, seeing what's being called. It can help you if you do memory, you can start to see what's being allocated a lot. And look, lang.reflect method is being allocated a lot. Hmm, maybe that's why we're, look, it's still growing. So maybe that's why we're creating a lot of garbage, might want to get rid of that reflection. We're allocating a lot of char arrays, which is probably because we got strings or something. Field, another reflection thing. So if you're worried about the memory, you might want to get rid of the reflection, which Clojure will do. Oh, look, it's also got the method arrays.
So it might be worth it to do that. All right, I'm going to stop this. All right, so that's a visual thing. You're not going to be able to run a script or something using this, it's a GUI. But if you need to get in there personally, this is actually a good tool. So I'm going to leave this open, because there's some tool that we can look at, that will do this all in the command line. So the first one is jstat.
The Java Virtual Machine Statistics Monitoring Tool. Okay, and it's got a bunch of options. I'm not going to go into them. But what it does is, it can monitor statistics about your class loader, that stuff we saw on the first screen. Where is it? This overview, sorry, monitor. This is showing memory usage, how many classes are loaded, how many threads you got. All of that is in jstat. As in jstat. The next one we're going to look at is jmap. There we go. So jmap, this one gives you your heap usage. This is going to give you how much memory is being used. You can actually dump the heap, and see, here it is, dump. You can get the whole thing in binary.
Wow, that's fun. You might need that for monitoring. So these are tools that you can build monitoring systems on top of. And you can see how many objects are instantiated for each class, that kind of thing. If you got a lot of JVMs, and some are running, and you don't want to take them down, you can use jinfo, another tool. These all come with Java, with the JDK. And this will print out information about running JVM, how it's configured, how big the heap is, how big the PermGen space is, those kinds of things. It'll just print it out. So I'm going to try it. I'm going to try jinfo. I forgot I haven't been trying.
So jinfo, and you're going to need the process ID, which I'm going to go and borrow from here, 41694. 41694. Oh, it needs my password, look at that. There we go. So we see all this stuff about it. Wow, so much stuff. The timezone I'm in. Nice. VM Flags. Wow, all that stuff. That's great. If you need it, that's awesome. I never actually needed that, but you know, one day maybe. Now, jstack. Hmm, jstack, so remember we can see, in this VisualVM, we can see the sampler, and see what's ... It makes a stack trace for each thread, and adds it all up, and figures out how much time is being spent in each thing, aso how much memory. So you can do the same thing at the command line. Although, I believe it only does it for one sample. So that's, jstack. Jstack. I want to try this like that, jstack.
There we go. This thread is waiting, this thread is waiting, waiting, waiting. Time's waiting, runnable ... So there, you see the whole stack here. It looks like a stack trace from an exception that gets thrown. It's the same thing, expect it's not an exception, it's actually, this is what's at the top right now. If you're in an infinite loop, you might be able to see that with this tool. Not as useful as sampling over time. But still useful. Okay, so those are your monitoring options if you're trying to debug something that's going wrong, either in development, or in production, just to figure some more information to help you solve your problems, or whatever you're trying to do on your JVM.