New: see how many SpigotMC page viewers end up running your plugin →

Performance

See how much of each 50 ms tick your plugin uses on real servers, which methods use it, and which release made it slower. No code to add.

What you see

Performance shows how many milliseconds per tick your plugin uses on average, the most expensive methods first, your event handlers and commands with calls, average, p95 and max, and a comparison between versions so a slow release stands out. When a new version uses clearly more time per tick than the one before it, you get a notification.

Main-thread sampling

About ten times a second (every 80 to 120 ms, jittered so it doesn't line up with ticks), a low-priority thread looks at what the server's main thread is running. When it's inside your plugin's code, the time since the last look is counted for that spot. Over an hour this adds up to where your plugin spends its main-thread time: scheduled tasks, listeners, commands and everything they call. It's the same idea as a sampling profiler like spark, kept light enough to leave on.

Each sample is named Entry > Inner: the outermost method of your plugin on the stack (the task, listener or command that started it) and the innermost one (where the time actually goes).

NameMeans
ScoreboardTask#run > Boards#renderYour scheduled task spends its time in Boards.render.
ArenaListener#onMoveThe time is in the listener method itself.
ArenaManager#tick·lambdaA lambda inside ArenaManager.tick.
Arena$1#runAn anonymous class inside Arena.

Only classes in the package of your main class and its sub-packages are recognized as your plugin. The SDK's own classes are left out.

Names show the class relative to your plugin's main package (mob.MobManager#tick), shortened for display. If your plugin is obfuscated, upload its mapping and they read as in your source. Obfuscated plugins

Timed event handlers and commands

On the first tick after start, and again after every report, your plugin's event handlers and plugin.yml commands are wrapped with a timer: two clock reads per call. They appear as ArenaListener · PlayerMoveEvent (with (async) for async events) and /arena, with calls, average, an estimated p95 and the slowest call.

Name a block of code (optional)

// Around any block
try (PluginAnalytics.Span span = analytics.time("arena_tick")) {
    tickArenas();
}

// Or with a lambda
analytics.time("load_arenas", () -> arenaManager.loadAll());

Span names follow the event naming rules; a span with an invalid name is ignored. Spans work on any thread and on Folia.

Server tick time

On Paper and its forks the server's average tick time (MSPT) is read every 5 minutes and shown for context: your share of a tick means more on a server that's already struggling. Spigot doesn't expose it.

Turn it off

analytics = PluginAnalytics.start(this, "pa_your_key");
analytics.autoPerformance(false); // no sampling, no automatic timing

Call it right after start: the wrappers are added on the next tick, and handlers already wrapped keep their timer. Automatic command events come from the same wrapper, so they stop too; spans keep working.

Limits

  • Up to 160 measured names per server per hour; method names are cut at 46 characters.
  • On Folia there's no single main thread: nothing is sampled or timed automatically, and MSPT isn't read. Spans still work.
  • A plugin whose main class is in the default package can't be told apart from the server, so sampling is off.
  • Taking a stack trace briefly pauses the main thread at a safepoint, ten times a second. It's far below what a tick can notice, but autoPerformance(false) turns it off completely.