I have written this small (and brutally inefficient) class and wanted to profile it using the Java VisualVM.
public class Test {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
int n = Integer.parseInt(args[0]);
int fib = fib(n);
System.out.println(fib);
}
private static int fib(int n) {
if (n < 2) {
return n;
}
return fib(n-1)+fib(n-2);
}
}
The results are bizarre. The results are completely dominated by calls to ConnectionHandler.run().
(98.2%) sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run()
(1.7%) java.lang.Thread.join(long)
(0%) java.lang.String.equals(Object)
etc...
There are probably around one hundred methods profiled and not one of them is fib(int)!
It is inconceivable that my program is actually spending all of its time in these methods. They appear to be the profiler connecting to my jvm and doing its thing.
What am I doing wrong?
Edited for clarity: If you pass in 45 for the n this application runs for 20 well profiled seconds. The program I was profiling originally (not a fibonacci calculator) pegs all four cores on my cpu at 100% and I was doing profiling runs lasting up to 5 minutes. These had the same results and methods from my application did not appear high up on the hot spot method list.
It varies from run to run but ConnectionHandler.run() is always at the top and usually accounts for ~99% of the profile time.
Second Edit: I have tried using the sampler and I am now getting results that are consistent with what JProfiler is producing. The downside to this is that I don't get the stack trace information that comes with profiling. But for my immediate needs this is excellent.
Something I have discovered while playing around is that VisualVM counts wall-clock time for method calls while profiling them.
In my specific case my application has a main thread which launches worker threads and immediately blocks waiting for a message on a queue.
This means that the blocking method will appear to take up almost all of the time on the profiler despite the fact that it is not this method that is eating up my CPU.
I would expect that the same is true of the sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run() method which does its job nicely - but when it terminates it becomes one of the longest running methods in my application - repeatedly.
Java VisualVM is a tool that provides a visual interface for viewing detailed information about Java applications while they are running on a Java Virtual Machine (JVM), and for troubleshooting and profiling these applications.
Java VisualVM enables you to take profiler snapshots to capture the results of a profiling session. A profiler snapshot captures the results at the moment the snapshot is taken. To take a snapshot, click the Take Snapshot of Collected Results button in the toolbar while a profiling session is in progress.
Use VisualVM bundled with GraalVMGraalVM contains a fully compliant Java SE 8, Java SE 11, and Java SE 17 JDK distribution based on Oracle JDK and OpenJDK. It features an innovative JIT compiler which may noticeably improve performance of Java applications, compared to the standard Oracle JDK or OpenJDK.
What is VisualVM. It is a tool automatically available after JDK is installed. The executable file could be found on your <JDK installation folder>/bin as displayed below. In order to measure the performance of your application, it is necessary for the application to be recognized by VisualVM first.
I don't think that's inconceivable at all. You have an application where the "payload" is fairly minuscule (though that of course depends on the value of n
), and you have to accept that the extra effort required (to connect the profiler and shift all the information across to it) will swamp that payload.
This is not the sort of application I would be profiling in the first place since it's pretty obvious that the vast amount of time would be spent in fib
anyway (for non-trivial values of n
), marking that as an obvious target for optimisation.
I would be more inclined to use the profiler for more substantial applications where:
If you really want to test that code, you probably need to bump up its effect by (for example) replacing:
int fib = fib(n);
with:
for (int i = 0; i < 100000; i++) {
int fib = fib(n);
)
I'll tell you one thing to watch out for though. I don't know the internals of any particular JVM but using a recursive method where the reduction of the argument is slow is usually a bad idea, one that leads to stack space being exhausted pretty quickly.
By that, I mean a binary search is a good candidate since it removes half the remaining search space with each recursion level (so that a search space of a billion items is only 30 levels).
On the other hand, using recursion for a Fibonacci sequence on the number 1,000,000,000 would take about a billion levels and most stacks would have a hard time containing that.
Tail end recursion optimisation may avoid that problem but you need to be careful in case that optimisation isn't done.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With