Я написал этот небольшой (и жестоко неэффективный) класс и хотел его профилировать с помощью 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);
}
}
Результаты выглядят странно. В результатах полностью доминируют вызовы ConnectionHandler.run().
(98.2%) sun.rmi.transport.tcp.TCPTransport $ConnectionHandler.run()
(1,7%) java.lang.Thread.join(long)
(0%) java.lang.String.equals(Object)
и т.д...
Есть, вероятно, около ста методов профилированных, и ни один из них не является fib (int)!
Невозможно представить, что моя программа фактически проводит все свое время в этих методах. Они кажутся профайлером, подключающимся к моему jvm и выполняющим его.
Что я делаю неправильно?
Отредактировано для ясности:. Если вы передадите 45 для n, это приложение будет работать в течение 20 хорошо профилированных секунд. Первоначально программа, которую я профилировал (а не фибоначчивый калькулятор), привязывала все четыре ядра к моему процессору на 100%, и я делал профайлинговые прогоны продолжительностью до 5 минут. Эти же результаты и методы из моего приложения не отображались высоко в списке методов горячей точки.
Он варьируется от run to run, но ConnectionHandler.run() всегда находится наверху и обычно составляет ~ 99% от времени профиля.
Second Edit: Я попытался использовать сэмплер, и теперь я получаю результаты, которые согласуются с тем, что производит JProfiler. Недостатком этого является то, что я не получаю информацию о трассировке стека, которая поставляется с профилированием. Но для моих непосредственных потребностей это превосходно.
Что-то, что я обнаружил во время игры, - это то, что VisualVM учитывает настенные часы для вызовов методов при их профилировании.
В моем конкретном случае у моего приложения есть основной поток, который запускает рабочие потоки и немедленно блокирует ожидание сообщения в очереди.
Это означает, что метод блокировки, по-видимому, будет занимать почти все время на профилировщике, несмотря на то, что это не тот метод, который ест мой процессор.
Я бы ожидал, что то же самое относится к методу sun.rmi.transport.tcp.TCPTransport $ConnectionHandler.run(), который выполняет свою работу красиво - но когда он завершается, он становится одним из самых длинных запущенных методов в моем приложении - повторно.