Profiler Spark: jak znaleźć przyczynę lagów na serwerze Minecraft (2026)

Profiler Spark: jak znaleźć przyczynę lagów na serwerze Minecraft (2026)

Serwer się ślimaczy, TPS skacze koło 14, na czacie posypały się skargi na "lagi", a ty siedzisz i nie wiesz, co właściwie zżera tick. Kilka lat temu wpisywałeś /timings paste i w minutę miałeś winowajcę. Dziś Timings w Paperze jest praktycznie martwy (domyślnie wyłączony od 1.20.5, całkowicie usunięty w 1.21+), a Aikar's Timings v2 jako osobny produkt od lat nie jest rozwijany. Pałeczkę przejął Spark od lucko i przez ostatnie dwa lata stał się standardem branżowym: poleca go zespół PaperMC, wymieniają go w bugreportach Mojanga, wspierają autorzy Purpura, Folii, Fabrica i większości dużych modpacków.

Ten artykuł pokazuje, jak postawić Spark w 2026, jak zebrać sensowny profil z produkcyjnego serwera, jak czytać flame-graph bez zaklęć i jak z drzewa wywołań wyciągnąć konkretnego sprawcę spadku TPS, czy to plugin, chunk czy garbage collector.

Czym jest Spark i dlaczego wyparł Timings

Spark to sampling profiler dla JVM, dostrojony pod Minecrafta. Zamiast instrumentować kod jak stary Timings, ze stałą częstotliwością robi snapshoty stosu wszystkich wątków serwera. Z tych snapshotów buduje się drzewo wywołań i flame-graph, po którym widać, gdzie serwer naprawdę spędza czas CPU.

Kluczowa różnica wobec Timings: Spark nie mówi "plugin X zużył N ticków na obsługę zdarzeń". Mówi "ta dokładnie metoda, w tym dokładnie stosie, była gorąca na CPU". To daje uczciwy obraz, włącznie z tym, czego Timings nigdy nie widział: wnętrza Mojanga, ciężka logika w pluginach, lambdy, kod natywny, pauzy GC.

Dlaczego Timings zszedł ze sceny: był wbudowany w Paper, ciągnął własny narzut, mierzył tylko to, co specjalnie owinięto timerem, zniekształcał obraz przy kodzie asynchronicznym i nie łapał GC. Paper 1.20.5 wyłączył Timings domyślnie, 1.21 usunął go. Spark w międzyczasie działa na każdej wersji od 1.8 do 1.21.x, na Spigocie, Paperze, Folii, Fabricu, Forge, NeoForge, Velocity i BungeeCord.

Autorem Sparka jest lucko, opiekun LuckPerms. Rozwój odbywa się otwarcie na github.com/lucko/spark, oficjalna strona spark.lucko.me, dystrybucja przez Hangar (hangar.papermc.io/lucko/spark) i Modrinth (modrinth.com/plugin/spark). Na kwiecień 2026 aktywna jest gałąź 1.10.x, wspierane Java 21 i serwery do 1.21.5.

Instalacja na różnych platformach

Spark dostarczany jest jako jedno uniwersalne JAR z autodetekcją platformy plus osobne buildy dla Fabrica i Forge. Pobieraj ze spark.lucko.me/download lub Modrinth, nigdy z reuploadów.

Spigot, Paper, Purpur, Folia. Wrzuć spark-1.10.x-bukkit.jar do plugins/ i restart serwera. Po starcie /spark jest dostępne pod uprawnieniem spark. Domyślnie tylko dla operatorów; dla nie-OP graczy nadaj uprawnienie przez LuckPerms: lp user <name> permission set spark true.

Velocity. Na proxy spark-1.10.x-velocity.jar w plugins/. Na proxy Spark szczególnie przydatny, żeby łapać lagi w obsłudze plugin-messages, w login-flow, w LiteBans, ChatRegulator i podobnych ciężkich pluginach.

BungeeCord i Waterfall. Jest osobne spark-1.10.x-bungeecord.jar. Te same komendy, te same flagi. Pamiętaj, że Bungee/Waterfall są w 2026 traktowane jako legacy, migracja na Velocity jest rekomendowana.

Fabric. Quilt i Fabric dzielą spark-1.10.x-fabric.jar w mods/, zależność od Fabric API. Dla instalacji tylko serwerowej weź build serwerowy; dla klienta jest osobny artefakt spark-fabric-client.

Forge i NeoForge. Forge 1.20.1 i starsze: spark-1.10.x-forge.jar w mods/. NeoForge 1.20.4+ ma własne JAR spark-neoforge. Komendy identyczne, dostęp tylko dla operatora domyślnie, drobniejsze uprawnienia przez FTB Ranks.

Sponge. Przez spark-1.10.x-sponge.jar. SpongeForge bierze go w mods/, SpongeVanilla w plugins/.

Po instalacji wpisz /spark żeby sprawdzić, że pojawia się pomoc. Jeśli "unknown command", sprawdź latest.log, plugin może marudzić na za starą Javę (wymagane 17+, zalecane 21).

Podstawowe komendy: /spark profiler, /spark tps, /spark health

Spark to zestaw subkomend pod wspólnym /spark. Te są używane w 95% przypadków.

/spark tps

Pokazuje aktualny TPS i MSPT (milisekundy na tick), rozbity po interwałach:

TPS from last 5s, 10s, 1m, 5m, 15m:
  *14.2, *16.8, 19.4, 19.8, 19.9
Tick durations (min/med/95%ile/max ms) from last 10s, 1m:
  3.2 / 12.4 / 48.1 / 220.5

Gwiazdka oznacza wartości poniżej celu. 95. percentyl MSPT to najużyteczniejsza liczba: ignoruje średnią i pokazuje co 95% ticków pobiło. Jeśli 95%ile siedzi powyżej 50ms na stałe, masz problem, nawet jeśli średni TPS wygląda ładnie.

/spark profiler

Główna komenda. Startuje sampling profiler, domyślnie na głównym wątku serwera. Bez argumentów działa do ręcznego stopu przez /spark profiler stop.

/spark profiler start
... czekamy 60-300 sekund podczas lagów ...
/spark profiler stop

Po zatrzymaniu Spark ładuje wynik na bytebin (publiczny pastebin binarny) i wrzuca link na czat w stylu https://spark.lucko.me/abc123def. Otwierasz w przeglądarce i pracujesz z interaktywnym viewerem.

Przydatne flagi:

  • --timeout 120, automatyczny stop po 120 sekundach. Wygodne, gdy lag się powtarza.
  • --thread *, profiluj wszystkie wątki JVM, nie tylko Server Thread. Konieczne, gdy podejrzewasz async pluginy, GC, Netty.
  • --only-ticks-over 100, zapisuj stosy tylko z ticków dłuższych niż 100ms. Złoty tryb dla rzadkich freezów w środku zdrowego TPS.
  • --interval 10, sampling co 10ms (domyślnie 4ms). Mniejszy narzut na długich nagraniach.

/spark health

Nowsza komenda, zastąpiła /spark tickmonitor. Szybki przegląd JVM:

Server Health Report:
  TPS: 18.4 (last 5s)
  CPU: 64% (process), 82% (system)
  Memory: 8.4G / 12G heap
  GC: G1 Young 12 collections, 340ms total
  Disk: /world 240G free of 500G

Jeśli system CPU wysoki a process CPU niski, na maszynie ktoś inny zżera CPU, klasyka shared hostingu.

/spark heapsummary i /spark heapdump

/spark heapsummary to lekki snapshot: ile obiektów każdej klasy żyje w heapie i ile ważą. Wystarczy, żeby złapać memory leak pluginu: 480000 instancji org.bukkit.craftbukkit.v1_21_R1.entity.CraftItem znaczy, że ktoś nie sprząta dropów.

/spark heapdump to pełny hprof dump dla VisualVM lub Eclipse MAT. Uruchamiaj tylko gdy heapsummary nie odpowiedział: dump idzie na dysk, może mieć gigabajty, a serwer zatrzymuje się na czas zapisu.

/spark gc

Statystyki garbage collectora od startu serwera. Przydatne, żeby porównać ile wall-clock JVM spędziła w pauzach GC kontra realna praca. Powyżej 5% to zła wiadomość, powyżej 15% oznacza zepsute flagi Javy lub memory leak do złapania.

Czytanie flame-graph: Self time vs Total time

Graph viewer na spark.lucko.me to odwrócony flame-graph: korzeń wywołań na górze, liście na dole. Każdy prostokąt to metoda, szerokość proporcjonalna do czasu spędzonego w niej. Szerszy znaczy gorętszy.

Total time obejmuje czas w samej metodzie plus wszystkie wywołania w dół. Korzeń ma zawsze 100%. Używaj Total, żeby wybrać gałąź ciągnącą najwięcej CPU jako całość.

Self time liczy tylko czas bezpośrednio w metodzie, bez potomków. Zawęża się do prawdziwych gorących liści. To po Self time łapie się zły kod.

Praktyczna reguła: patrzysz na Total, żeby wybrać gałąź ("60% czasu w ChunkMap.tick"), przełączasz na Self i schodzisz po gałęzi w dół, aż Self osiągnie maksimum. Liść, gdzie Self szczytuje, to konkretna problemowa funkcja.

Spark koloruje prostokąty według źródła kodu: Mojang/NMS w jednym odcieniu, pluginy w innym, JVM w trzecim. Włącz "Colour by source" w ustawieniach viewera i jeden źle zachowujący się plugin świeci się na czerwono. Ctrl+F szuka po pakiecie (com.discordsrv), klasie (AsyncCatcher) lub nazwie metody. Zakładka "Sources" grupuje całe drzewo po pochodzeniu, od razu widać top-3 winowajców.

Typowi sprawcy lagów i jak ich widać w Sparku

Ładowanie i generacja chunków. Ścieżka w drzewie: ServerLevel.tick -> ChunkMap.tick -> ChunkGenerator.generate. Jeśli ta gałąź zżera 30%+ Total, winni są albo gracze eksplorujący nowe tereny, albo plugin sam ładujący chunki (dynmap, anti-AFK teleporty, reklamowe pluginy z auto-tp). Naprawa: pre-generacja przez Chunky, view-distance ograniczone do 8-10, wyłączenie wymuszonego ładowania chunków w pluginach trzecich.

Tick-list encji. Gałąź EntityTickList.forEach -> Mob.tickAi -> PathFinder.findPath. Pathfinding na 20%+ zazwyczaj oznacza zaklinowane moby (przepełniona zagroda krów) lub villagerów ze zepsutą AI. Diagnostyka przez /spark profiler --only-ticks-over 100, potem kill @e[type=cow,distance=..200] dla konkretnej zagrody albo włączenie Lobotomize villagers w Purpurze.

Redstone. ServerLevel.tickBlock -> RedstoneWireBlock.updateState. Ta gałąź zapala się na serwerach technicznych i factionowych. Naprawa: redstone-implementation: ALTERNATE_CURRENT w paper-world-defaults.yml, zamiana zegarów na obserwatory lub komparatory.

Garbage collector. GC widać jako szerokie prostokąty w wątkach G1 Young Gen, G1 Concurrent, ZGC Concurrent. Jeśli profil z --thread * pokazuje, że GC zajmuje 10%+ wall-clock, flagi Javy są źle skonfigurowane albo masz za mało RAM. Sprawdź czy -Xms równa się -Xmx, próbuj G1 z aikar-flagami lub ZGC na Javie 21.

Plugin-łobuz. Klasyka: pod --thread * znajdujesz jeden plugin w wątku pool-7-thread-1, który stale pali CPU na Socket.connect, JDBC.executeQuery lub URL.openStream. To ten "asynchroniczny" plugin łomocący zewnętrzne API na każde event. Znajdź nazwę pluginu w stosie, wyłącz lub złorzecz autorowi.

Auto-save. Co 5-10 minut w profilu pojawia się duży skok Self time przy ChunkSerializer.write i RegionFile.write. Do pewnego stopnia normalne, ale jeśli auto-save zwala TPS z 20 do 10, dysk jest wolny (HDD zamiast NVMe) lub świat za duży i czas na paper trim albo chunky trim.

Jak udostępnić raport

Gdy tylko profiler się zatrzyma, na czacie/konsoli pojawia się link do bytebina typu https://spark.lucko.me/abc123def. Każdy z linkiem widzi interaktywny viewer.

Co warto wiedzieć:

  • Linki bytebin żyją długo, ale nie wiecznie. Jeśli profil potrzebny na dłużej, kliknij "Download" w viewerze i zapisz plik .sparkprofile.
  • Profile są publiczne, link otwiera się bez autoryzacji. Ze stosów wyciekają nazwy pakietów i klas, czasem nazwy graczy przez lambdy, czasem ścieżki w systemie plików.
  • Viewer ma przełącznik "Anonymize", maskujący nazwy graczy i ścieżki.

Dla wsparcia od autorów pluginów: link bytebin to dokładnie to, czego ludzie na Discordach PaperMC, EssentialsX czy dowolnego dużego pluginu zażądają. Bez screenshotów, bez wycinania linijek z logów, wystarczy link.

Cheatsheet komend Sparka

KomendaCo robiKiedy używać
/spark tpsAktualny TPS + MSPT po interwałachSzybki sanity check
/spark profiler startStart profileraDiagnostyka lagów na głównym wątku
/spark profiler stopStop + upload raportuPo zebraniu wystarczającej próbki
/spark profiler --timeout 120Auto-stop po 120sLag powtarzalny, set and forget
/spark profiler --thread *Wszystkie wątki JVMPodejrzenie async pluginów, GC, Netty
/spark profiler --only-ticks-over 100Tylko ticki dłuższe niż 100msRzadkie freezy w zdrowym TPS
/spark profiler --interval 10Sampling co 10msDługie nagrania, mniejszy narzut
/spark profiler infoStatus bieżącego profileraPotwierdzić że nagrywa
/spark profiler cancelAnulowanie bez raportuZmiana decyzji
/spark healthZdrowie JVM: CPU, RAM, GC, dyskPierwsze spojrzenie przy skargach
/spark heapsummaryLekki spis obiektów w heapiePolowanie na memory leaki
/spark heapdumpPełny hprof na dyskGłęboka analiza w MAT
/spark gcStatystyki GC od startuDiagnostyka pauz GC
/spark pingPing graczy serwerowyTriage sieciowy
/spark activityLog ostatniej aktywności pluginówCo i kto ostatnio uruchamiał

Zaawansowane opcje

Porównanie profili. Viewer ma przycisk "Compare", robiący diff dwóch nagrań. Przydatne po optymalizacji: nagrywasz "przed" i "po", widzisz która gałąź faktycznie spadła. Czasami widać, że naprawiłeś jedno, a obciążyłeś drugie, np. wyłączyłeś dynmap, ale Essentials zaczął częściej pisać na dysk.

Profilowanie Folii. Folia, wielowątkowy fork Papera, ma kilka Region Threads. Spark łapie każdy, viewer pokazuje je jako osobne drzewa. Na Folii szczególnie przydatny --only-ticks-over, żeby znaleźć który region zostaje w tyle.

Konfiguracja. Plik plugins/spark/config.json pozwala zmienić URL bytebina (dla self-hostowanych instancji), wymusić prywatne URL-e viewera (viewer-url-only-private: true) i włączyć automatyczne nagranie profilu przy spadku TPS poniżej progu:

{
  "profiler": {
    "auto": {
      "enabled": true,
      "trigger-tps": 10.0,
      "duration": 90
    }
  }
}

Z tą konfiguracją Spark sam łapie kolejny freeze, nie trzeba pilnować.

REST API. Spark publikuje REST API przez bytebin, więc nagrywanie i przechowywanie da się zautomatyzować. lucko ma przykładowy skrypt w repo, wygodne dla dużych sieci.

Porównanie z Timings i Aikar's Timings v2

AspektTimings (Paper)Timings v2 (Aikar)Spark
Status w 2026Usunięty w Paper 1.21+NierozwijanyAktywnie rozwijany
MetodaInstrumentacjaInstrumentacjaSampling
Pokrycie NMSOgraniczoneOgraniczonePełne
Wątki asyncNieCzęściowoPełne
Widzialne pauzy GCNieNieTak
NarzutŚredniŚredniNiski (1-2%)
Wsparcie FoliiNieNiePełne
Fabric/ForgeNieNiePełne
Udostępnianie raportutimings.aikar.cotimings.aikar.cospark.lucko.me
Diff profiliNieNieTak
Analiza heapNieNieTak

Praktyczny wniosek: jeśli wciąż masz Timings (na Paperze 1.20.4 lub starszym), przejdź na Sparka dziś. Trzymanie obu nie ma sensu, narzut się sumuje. Aikar's Timings v2 jako osobny produkt można zapomnieć, autor sam przeszedł na Sparka i poleca go w swoich poradnikach o flagach Javy.

FAQ

Czy Spark spowalnia serwer?

Sampling profiler przy domyślnym interwale 4ms kosztuje około 1% CPU. Mniej niż aktywny Timings kiedykolwiek kosztował. Na profilach dłuższych niż pięć minut różnica TPS pozostaje w granicach szumu statystycznego.

Czy mogę trzymać Sparka załadowanego cały czas?

Tak, wielu tak robi. Sam plugin niczego nie profiluje dopóki nie odpalisz komendy. Narzut czuwającego Sparka jest w zasadzie zerowy. /spark health działa w tle bez nagrywania stosów.

Co jeśli bytebin jest niedostępny?

Możesz zapisać profil lokalnie przez --save-to-file albo postawić własną instancję bytebina (źródła otwarte na GitHubie lucko). Jest też lokalny viewer: pobrany .sparkprofile otwierasz bezpośrednio w przeglądarce na spark.lucko.me, plik nie idzie na serwer.

Czy Spark pokaże DDoS?

Nie. Spark to profiler JVM, widzi co serwer sam robi. Zewnętrzne obciążenie sieciowe (UDP-flood, login-bot atak) jest dla niego niewidoczne. Do ochrony przed DDoS potrzeba filtracji sieciowej przed serwerem, takiej jak MineGuard, wtedy Spark potwierdzi że lag nie pochodzi z pluginu, tylko z presji zewnętrznej.

Jaki profil wysyłać autorowi pluginu w bugreporcie?

Minimum 60 sekund podczas realnego obciążenia, z --thread *, plus snapshot z /spark health. Jeśli problem rzadki, dodaj --only-ticks-over 100. Podaj wersję serwera, wersję pluginu i liczbę online w momencie nagrania.

Czy Spark działa dla klienta Minecrafta?

Tak, są dedykowane buildy spark-fabric-client i spark-forge-client. Przydatne dla twórców modpacków i streamerów polujących na FPS. Te same komendy, odpalane przez keybind lub czat.

Jeśli po pół godzinie z flame-graph trafisz w ścianę "wszystko gore, nic nie widać", to normalne, profilowanie to umiejętność. Nagrywaj często, porównuj przed i po każdej zmianie, buduj rozpoznanie wzorców. Po kilkunastu raportach zaczniesz rozpoznawać ChunkMap.tick i SpongeRedstone z dziesięciu metrów. Jeszcze jedno: Spark pokaże wewnętrzne hamulce, ale jeśli lag bierze się z obciążenia zewnętrznego (atak botów, UDP-flood, syntetyczny ping), profiler będzie milczał, bo winny nie jest serwer tylko sieć, a tam ochrona sieciowa pokroju MineGuard robi robotę, której Spark nie wykona.


Chroń swój serwer przed atakami DDoS

Darmowa ochrona z konfiguracją w 5 minut. 1 TB ruchu w zestawie.

Wypróbuj za darmo


Powiązane artykuły