運営している工業化マイクラサーバが頻繁に落ちるので対策した

1年越しの対策になってごめんなさい

TL;DR

GC関係とかヒープ関係のオプションをとりあえずたくさんつけてみた。

変更前

C:\Java\jre8\bin\java -Xms2048M -Xmx8192M -jar forge-universal.jar nogui

変更後

C:\Java\jre8\bin\java -server -Xms8192M -Xmx8192M -Xmn4096M -Xss4096M -XX:ParallelGCThreads=8 -XX:ConcGCThreads=4 -XX:CompressedClassSpaceSize=1024M -XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=512M -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+DisableExplicitGC -XX:UseGCOverheadLimit -XX:UseLargePages -jar forge-universal.jar nogui

ちょっとデフォルト値がどうなってるかはあまり確認してない、なんとなく安定稼働してるような気はする。

環境

最大でアクティブ6人くらいの小規模工業サーバです。 OS全体で見てライセンス問題ない範囲の接続数です(最大20)。

マシン

項目
OS Windows 10 Professional
CPU Ryzen 7 2700X
RAM 32GB
Java Oracle JRE 8u232

イクラサーバ

項目 備考
バージョン 1.12.2
サーバエンジン Minecraft Server 1.12.2 公式
Forge Mod Loader 1.12.2-14.23.5.2838
SpongeForge 1.12.2-2825-7.1.6
BuildCraft 7.99.24.1
IndustrialCraft 2 2.8.1.159-ex112
LogisticsPipes 0.10.1.136
CodeChickenLib 1.12.2 依存MOD
Forgelin 1.8.2 依存MOD

確認された現象

いろいろある

  • サーバを起動して放っておくとクラッシュ
  • 一人しかいない状態でIC2のテレポータでテレポートしたらクラッシュ
  • 戦闘キル時にクラッシュ

クラッシュの直接的な原因

  • 1tick にかかる時間のしきい値が超えている。
    • 1tick あたり server.propertiesmax-tick-time ミリ秒を超えると ServerHangWatchdog によって自動保存され、System.exit(1) が呼び出されるみたい。
    • 試しに max-tick-time=360000 にしてみたけどしっかり360秒かかってクラッシュしてるよう
java.lang.Error: ServerHangWatchdog detected that a single server tick took 360.00 seconds (should be max 0.05)
    at java.util.IdentityHashMap.get(IdentityHashMap.java:337)
    at net.minecraftforge.registries.GameData$BlockCallbacks$1.get(GameData.java:387)
    at net.minecraftforge.registries.GameData$BlockCallbacks$1.func_148747_b(GameData.java:382)
    at net.minecraft.world.chunk.BlockStateContainer.func_186017_a(BlockStateContainer.java:136)
    at net.minecraft.world.chunk.storage.AnvilChunkLoader.func_75820_a(AnvilChunkLoader.java:324)
    at net.minecraft.world.chunk.storage.AnvilChunkLoader.func_75816_a(AnvilChunkLoader.java:173)
    at net.minecraft.world.gen.ChunkProviderServer.func_73242_b(ChunkProviderServer.java:202)
    at net.minecraft.world.gen.ChunkProviderServer.func_73156_b(ChunkProviderServer.java:801)
    at net.minecraft.world.WorldServer.func_72835_b(WorldServer.java:207)
    at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:756)
    at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:397)
    at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:668)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:526)
    at java.lang.Thread.run(Thread.java:748)

クラッシュの間接的(根本的)な原因(推測)

じゃあサーバが完全に止まってしまう原因は?

工業要素が重たいのだとしたら定常的に負荷がかかるはず……

GCって重いよね

設定変えてみよっか

各オプションの説明

オプション 説明 備考
-server JVMをサーバモードで起動する デフォルト
-Xms8192M ヒープの初期サイズ ヒープの変動も重たいので最大サイズと同じにしておく
-Xmx8192M ヒープの最大サイズ 同上
-Xmn4096M ヒープのうちNew領域の初期サイズ ヒープサイズと同様に最大サイズと同じにしておく
-Xss4096M ヒープのうちNew領域の最大サイズ 同上
-XX:CompressedClassSpaceSize=1024M 圧縮されたクラス領域のサイズ OOM発生するようなら変更けど今回はデフォルト値
-XX:MetaspaceSize=512M メタ情報のサイズ
-XX:MaxMetaspaceSize=512M メタ情報の最大サイズ
-XX:+UseLargePages OSのLargePageを使用する Windowsで効果あるかは不明
-XX:+UseConcMarkSweepGC マークアンドスイープGCを使用する メインスレッドと並列して動作するのでメインのスループットは落ちるけど停止時間が短くなる
-XX:+CMSIncrementalMode マークアンドスイープGCのインクリメンタルモードを有効にする 細かい単位でGC実行されるようになり、停止時間が短くなる(Java8から非推奨らしい?)
-XX:ParallelGCThreads=8 GCスレッド数 デフォルトでは論理8コアまでは論理コア数、それ以上は(8+(論理コア数+8)*5/8)らしい?多すぎても少なすぎても効果が薄くなる
-XX:ConcGCThreads=4 マーク付けを行うスレッド数 デフォルトは(ParallelGCThreads+2)/4。多ければ多いほど強い?
-XX:+DisableExplicitGC System.gc()によるメジャーGCを無効化する
-XX:+UseGCOverheadLimit GCのオーバーヘッド制限を無効化する GC overhead limit exceededが発生しなくなる

参考文献

2021-06-29追記

また ServerHangWatchdog に捕まった。なんで Thread.sleep() !?

java.lang.Error: ServerHangWatchdog detected that a single server tick took 360.00 seconds (should be max 0.05)
    at java.lang.Thread.sleep(Native Method)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:530)
    at java.lang.Thread.run(Thread.java:748)