運営している工業化マイクラサーバが頻繁に落ちるので対策した
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.properties の
max-tick-time
ミリ秒を超えると ServerHangWatchdog によって自動保存され、System.exit(1)
が呼び出されるみたい。 - 試しに
max-tick-time=360000
にしてみたけどしっかり360秒かかってクラッシュしてるよう
- 1tick あたり server.properties の
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 が発生しなくなる |
参考文献
- フラグの簡単な説明
- ヒープの説明がわかりやすかった
- GCのオプションの説明わかりやすかった
- チューニングの方針やオプションの説明がわかりやすかった
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)