Dos servidores pueden correr el mismo jar, los mismos plugins y la misma cantidad de jugadores, y uno da tirones cada par de minutos mientras el otro no. La diferencia muchas veces no está en el servidor en absoluto. Está en cómo se le dijo al recolector de basura de Java que se comportara.
Qué le hace de verdad el recolector de basura a tu tick
Java gestiona la memoria por ti: los objetos que tu código deja de referenciar acaban siendo recuperados por el recolector de basura, en automático. El problema es que recuperar memoria es trabajo en sí mismo, y por defecto la JVM decide cuándo hacer ese trabajo según sus propias reglas heurísticas, pensadas para software genérico y no para un programa que tiene 50 milisegundos para terminar un tick y ni uno más.
Cuando el recolector decide que hace falta una pausa, todos los hilos —incluido el que corre tu mundo— se paran hasta que termina. Una pausa pequeña y frecuente es invisible. Un recolector que deja que se acumule basura y luego hace una recolección grande produce justo el síntoma que la gente describe como «el servidor se congela un segundo cada pocos minutos», y es el propio diseño del recolector haciendo eso, no un fallo.
Qué está haciendo de verdad un buen conjunto de flags
| Objetivo | Cómo se consigue |
|---|---|
| Un recolector de basura moderno | Cambiar a G1GC (por defecto en Java reciente, pero conviene confirmarlo) en vez de un recolector viejo pensado para rendimiento total antes que latencia |
| Pausas cortas y predecibles en vez de pocas largas | Un objetivo de tiempo de pausa máximo que le dice al recolector que haga recolecciones más frecuentes y pequeñas en vez de menos y grandes |
| Menos recolección en total | Tamaño de regiones y umbrales de porcentaje de heap ajustados al patrón de acceso de un servidor de juego y no al de una aplicación Java genérica |
El recolector de basura tiene que esforzarse más según se llena el heap, y un heap que está constantemente casi lleno va a pausar por muy bien afinado que esté el recolector: los flags cambian con cuánta elegancia se degrada, no cuánta memoria hay. Si afinar los flags no arregló un tirón, la siguiente pregunta honesta es si el plan tiene memoria suficiente para lo que corre de verdad, no qué otro conjunto de flags probar.
Aplicar un conjunto de flags
- Ponlos como argumentos de la JVM, antes de
-jarConfiguran el propio proceso de Java, no el servidor de Minecraft: el orden en el comando de inicio importa, y tienen que ir antes de nombrar el jar.
- Iguala
-Xmsy-Xmxal mismo valorUn heap al que se le deja crecer y encoger provoca trabajo extra de recolección solo por redimensionarse. Fijar los dos a la asignación del plan elimina ese coste por completo.
- Cambia una cosa y observa, no cinco
Los conjuntos de flags interactúan entre sí. Probar el conjunto entero de golpe te dice si ayudó; probar cada pieza por separado te dice cuál lo hizo.
Un servidor puede tener memoria de sobra según la gráfica del panel y aun así pausar, porque la pausa va de cómo está eligiendo el recolector recuperar memoria, no de si la memoria se acabó. Mirar la CPU junto a la memoria durante un tirón suele mostrar un pico breve en un núcleo —el recolector haciendo su trabajo—, que es la firma que hay que buscar.