Приложение тормозит. Где узкое место? Почему растёт память? Есть ли дедлок?
(Спойлер: обычно всё сразу, и обычно в пятницу после 17:00.)

Каждый Java-разработчик хотя бы раз сталкивался с необходимостью "заглянуть внутрь" работающей JVM. И каждый раз это требует:

  • знания утилит jcmd, jfr, jps;

  • ручного парсинга вывода;

  • переключения между терминалом, IDE и логами;

  • а главное — контекста, который сложно передать другому человеку (или ИИ).
    Особенно, если этот контекст держится на трёх чашках эспрессо и смутном воспоминании, что «где-то тут я менял пул потоков».

Что если бы ваш AI-ассистент мог сам запустить профилирование, проанализировать JFR-запись и сказать: "Вот метод, который аллоцирует 80% памяти, и вот стек вызовов"?

Я написал JavaPerf — MCP-сервер, который даёт ИИ-инструментам прямой доступ к нативным утилитам JDK для профилирования. Без графических интерфейсов, без ручного CLI — только диалог с ассистентом.

Что такое MCP и зачем это нам?

Model Context Protocol (MCP) — открытый стандарт от Anthropic, который позволяет подключать внешние инструменты к LLM-приложениям через единый интерфейс.

Проще говоря: MCP-сервер — это "мост" между вашим кодом/инфраструктурой и ИИ. Хост (например, Claude Desktop) отправляет запросы, сервер выполняет действия и возвращает структурированный результат.

Что мне нужно было от MCP для Java-профилирования:

  • Удобство: хранить контекст и jfr логи до/после, чтобы ИИ помнил историю вопросов и результатов профилирования.

  • Безопасность: только локальный запуск, закрытый контур и все такое, а так же контроль, какие команды может выполнять сервер.

  • Универсальность: один сервер работает в разных MCP-совместимых клиентах.

Знакомьтесь: JavaPerf

JavaPerf — это MCP-сервер на Node.js, которы�� оборачивает утилиты JDK (jcmd, jfr, jps) в набор инструментов, понятных ИИ-ассистенту.

git: https://github.com/theSharque/mcp-jperf

npm: https://www.npmjs.com/package/javaperf

Что умеет из коробки:

  • list_java_processes — найти нужный Java-процесс по PID или имени класса

  • start_profiling / stop_profiling — запустить и остановить JFR-запись с настройками settings=profile

  • parse_jfr_summary — получить сводку: топ методов, события GC, аномалии

  • profile_memory / profile_time / profile_frequency — три взгляда на данные: аллокации, CPU, частота вызовов

  • trace_method — построить дерево вызовов конкретного метода из .jfr

  • check_deadlock — возвращает JSON с потоками и блокировками. Если в ответе есть цикл — поздравляем, вы нашли тот самый дедлок, который «не может случиться по дизайну».

  • analyze_threads — диагностика блокировок и потоков

  • heap_histogram / heap_dump / heap_info — работа с кучей: от гистограммы до full .hprof-дампа — тот самый файл, который «всего 2 ГБ», но почему-то не помещается ни на один диск в нужный момент.

  • vm_info — быстрая справка по версии JVM, флагам, uptime

Все инструменты возвращают структурированный JSON, который ИИ легко интерпретирует и использует для дальнейших рассуждений.

Как установить и примеры конфигурации есть в гитхабе, нет смысла копипастить одно и тоже.

Сценарий из жизни: как это работает на практике

Представим, что ваше Spring-приложение начало потреблять больше памяти. Вот как может выглядеть диалог с ИИ-ассистентом, подключённым к JavaPerf:

  • Вы: Приложение myapp начинает тормозить через 2 часа работы. Давай найдём утечку памяти.

  • AI: Вижу процесс myapp (PID 31415). Запускаю 60-секундную JFR-запись...
    выполняет start_profiling с pid=31415, duration=60

    ...прошла минута...

  • AI: Запись завершена. Анализирую аллокации...
    вызывает profile_memory с топ-10 аллокаторов

    Результат:
    78% аллокаций создаются в com.example.service.DataProcessor.process()
    Внутри метода: new byte[1024*1024] в цикле без батчинга
    Рекомендация: использовать пул буферов или увеличить размер пакета

  • Вы: Покажи стек вызовов этого метода.

  • AI: вызывает trace_method с className=com.example.service.DataProcessor, methodName=process
    Вот дерево вызовов: Controller.handleRequest() -> Service.processBatch() -> DataProcessor.process() <- горячая точка

Всё это — без переключения на терминал, без ручного парсинга "jfr print". ИИ получает структурированные данные и строит гипотезы в контексте вашего диалога. Самый большой плюс ИИ в том, что он знает как и что искать, а еще он не устает от монотонной работы. Вы просто даете ему задачу и смотрите как он тратит ваши деньги выполняет ее, выдавая вам результат!

Технические детали: как устроен сервер

Архитектура:

Как-то так...
Как-то так...
  • Сервер реализует спецификацию MCP через библиотеку @modelcontextprotocol/sdk.

  • Каждый инструмент — это асинхронная функция, которая:

    1. Валидирует входные параметры (PID, длительность, пути).

    2. Вызывает соответствующую JDK-утилиту через child_process.

    3. Парсит вывод в структурированный JSON.

    4. Возвращает результат + человекочитаемое описание.

Обработка JFR

JFR-записи — это бинарный формат. Вместо ручного разбора мы:

  • Используем "jfr print --events" для извлечения ключевых событий.

  • Агрегируем данные по методам, потокам, аллокациям.

  • Выделяем аномалии: методы с высокой частотой вызовов, длительные блокировки, всплески GC.

Важно: из-за семплинга (~10 мс) очень быстрые методы могут не попасть в ExecutionSample.Если ваш метод выполняется быстрее 10 мс — возможно, он просто слишком хорош для этого мира. Или просто слишком быстрый, чтобы его заметили. В любом случае, для таких случаев есть trace_method.

Ограничения и как их обойти

  1. Локальность: сервер работает только на машине, где запущен.
    Для удалённых серверов используйте SSH-туннель или разверните MCP-хост рядом с приложением.

  2. Права доступа: пользователь, запустивший MCP, должен иметь доступ к целевой JVM.
    Запускайте от того же пользователя, что и приложение, или настройте jcmd-permissions.

  3. Производительность: heap_histogram с флагом all вызывает full GC.
    Используйте с осторожностью на продакшене; для мониторинга лучше heap_info.

  4. JFR-настройки: по умолчанию используется settings=profile (баланс детализации/оверхеда).
    Для продакшена можно передать кастомный .jfc-файл (планируется в будущих версиях).

Заключение

JavaPerf — это не замена YourKit или JVisualVM. Это интерфейс для диалога между разработчиком и системой профилирования, где ИИ выступает как «умный посредник», который знает, какие команды выполнить, как интерпретировать результат и что спросить дальше.

Если вы:

  • устали переключаться между терминалом и IDE при отладке производительности;

  • хотите, чтобы ваш AI-ассистент понимал контекст вашего Java-приложения;

  • верите, что будущее за инструментацией через естественный язык;

— попробуйте JavaPerf. Установка занимает 2 минуты, а первый профилировочный сценарий можно запустить прямо в диалоге с ИИ.