¿Cómo puedo evitar los retrasos de recolección de basura en juegos Java? (Mejores Prácticas)

Estoy actualizando juegos interactivos en Java para la plataforma Android. De vez en cuando hay un hipo en el dibujo y la interacción para la recolección de basura. Por lo general es menos de una décima de segundo, pero a veces puede ser tan grande como 200ms en dispositivos muy lentos.

Estoy usando el perfilador de ddms (parte del SDK de Android) para buscar de dónde provienen mis asignaciones de memoria y las excise de mis bucles internos de dibujo y lógica.

El peor delincuente había sido loops cortos hechos como,

for(GameObject gob : interactiveObjects) gob.onDraw(canvas); 

Donde cada vez que el bucle se ejecutó había un iterator asignado. Estoy usando arrays ( ArrayList ) para mis objetos ahora. Si alguna vez quiero árboles o hashes en un bucle interno sé que tengo que tener cuidado o incluso reimplementarlos en lugar de utilizar el marco de colecciones de Java ya que no puedo permitirme la recolección de basura extra. Eso puede venir cuando estoy mirando colas de prioridad.

También tengo problemas donde quiero mostrar las puntuaciones y el progreso usando Canvas.drawText . Esto es malo,

 canvas.drawText("Your score is: " + Score.points, x, y, paint); 

Porque las Strings , los arrays char y los StringBuffers se asignarán por todas partes para hacer que funcione. Si usted tiene algunos elementos de visualización de texto y ejecutar el marco 60 veces por segundo que comienza a sumarse y aumentará su cobro de recogida de basura. Creo que la mejor opción aquí es mantener char[] arrays y decodificar su int o double manualmente en él y concatenar cadenas en el principio y el final. Me gustaría saber si hay algo más limpio.

Sé que debe haber otros por ahí tratando esto. ¿Cómo lo manejas y cuáles son las trampas y las mejores prácticas que has descubierto para funcionar de forma interactiva en Java o Android? Estos problemas de gc son suficientes para hacerme perder la gestión de memoria manual, pero no mucho.

6 Solutions collect form web for “¿Cómo puedo evitar los retrasos de recolección de basura en juegos Java? (Mejores Prácticas)”

He trabajado en juegos móviles Java … La mejor manera de evitar objetos GC'ing (que a su vez activará el GC en un momento u otro y matará los perfs de tu juego) es simplemente evitar crearlos en tu juego principal En primer lugar.

No hay manera "limpia" de lidiar con esto y voy a dar un ejemplo …

Típicamente tiene, digamos, 4 bolas en la pantalla en (50,25), (70,32), (16,18), (98,73). Bueno, aquí está tu abstracción (simplificada por el bien de este ejemplo):

 n = 4; int[] { 50, 25, 70, 32, 16, 18, 98, 73 } 

Usted "pop" la segunda bola que desaparece, su int [] se convierte en:

 n = 3 int[] { 50, 25, 98, 73, 16, 18, 98, 73 } 

(Notamos que ni siquiera nos preocupamos por "limpiar" la cuarta bola (98,73), simplemente seguimos el número de bolas que nos quedan).

Seguimiento manual de los objetos, lamentablemente. Esto es lo que se hace en la mayoría de los actuales juegos Java de gran rendimiento que están disponibles en dispositivos móviles.

Ahora para las secuencias, aquí es lo que haría:

  • En la inicialización del juego, predraw usando drawText (…) sólo una vez los números de 0 a 9 que se guardan en una matriz BufferedImage[10] .
  • En la inicialización del juego, predraw una vez "Su puntuación es:"
  • Si la "Tu puntuación es:" realmente necesita ser rediseñado (porque, digamos, es transparente), luego vuelva a dibujar de su BufferedImage pre-almacenado
  • Loop para calcular los dígitos de la partitura y añadir, después de la "Su puntuación es:" , cada dígito manualmente uno por uno (copiando cada vez el dígito correspondiente (0 a 9) de su BufferedImage[10] donde pre- Almacenado.

Esto te da lo mejor de ambos mundos: obtienes la reutilización de la fuente drawtext (…) y creaste exactamente cero objetos durante tu loop principal (porque también esquivaste la llamada a drawtext (…) que en sí puede muy bien ser Crappily generando, bien, mierda innecesaria).

Otro "beneficio" de esta "puntuación de dibujo de creación de objetos cero" es que el almacenamiento y la reutilización cuidadosa de imágenes para las fuentes no es realmente "asignación / desasignación manual de objetos" , es realmente un almacenamiento en caché cuidadoso.

No es "limpio", no es "buena práctica", pero así es como se hace en los juegos móviles de primera categoría (como, por ejemplo, Uniwar).

Y es rápido. Darn rápido. Más rápido que cualquier cosa que involucre la creación de objetos.

PS: En realidad, si observas cuidadosamente algunos juegos para móviles, verás que a menudo las fuentes no son realmente fuentes de sistema / Java, sino fuentes perfectas de píxeles hechas específicamente para cada juego (aquí te acabo de dar un ejemplo de cómo guardar el sistema en caché / Fuente Java, pero obviamente también podría caché / reutilizar una fuente pixel-perfect / bitmapped).

Aunque esta es una pregunta de 2 años …

El único y el mejor enfoque para evitar el GC lag es evitar el propio GC asignando todos los objetos necesarios de forma estática (incluso al inicio). Pre-crea todo el objeto requerido, y nunca los hace para ser suprimido. Utilizar el agrupamiento de objetos para volver a utilizar el objeto existente.

De todos modos puede tener pausa eventual incluso después de hacer todas las optimizaciones posibles en su código. Porque cualquier otra cosa que tu código de la aplicación todavía está creando objetos del GC internamente que se convertirán en basura eventual. Por ejemplo, biblioteca base Java . Incluso el uso de la clase simple de la clase puede crear basuras. (Por lo que debe evitarse) Llamar a cualquiera de Java API puede crear garbages. Y estas asignaciones no son evitables mientras se utiliza Java.

Además, debido a que Java está diseñado para utilizar GC, tendrá problemas por la falta de características si realmente intenta evitar GC. (Incluso la clase List debe evitarse) Debido a que permite el GC, todas las bibliotecas pueden utilizar GC, por lo que prácticamente / prácticamente no tienen biblioteca . Considero que evitar GC en el lenguaje basado en GC es una especie de prueba de locura.

En última instancia, la única manera práctica es ir abajo al nivel más bajo donde usted puede controlar la memoria usted mismo completamente. Tales como C lenguas de la familia (C, C ++, etc). Así que ve a NDK.

Nota

Ahora Google está enviando incremental (concurrente?) GC que puede disminuir pausa mucho. De todos modos GC incremental significa sólo la distribución de la carga de GC a través del tiempo, por lo que todavía ve pausa eventual si la distribución no es ideal. Además, el rendimiento del GC en sí mismo se reducirá debido al efecto secundario de la reducción de los lotes y la sobrecarga de la operación de distribución.

He construido mi propia versión sin basura de String.format , al menos tipo de. Lo encuentras aquí: http://pastebin.com/s6ZKa3mJ (disculpe los comentarios alemanes).

Utilícelo de esta manera:

 GFStringBuilder.format("Your score is: % and your name is %").eat(score).eat(name).result 

Todo está escrito en una matriz char[] . Tuve que implementar la conversión de entero a cadena manualmente (dígito por dígito) para deshacerse de toda la basura.

Aparte de eso, utilizo SparseArray donde sea posible, porque todas las estructuras de datos de Java como HashMap , ArrayList etc. tienen que usar el boxeo para tratar tipos primitivos. Cada vez que coloca un int en Integer , este objeto Integer tiene que ser limpiado por el GC.

Si no desea procesar previamente el texto como se ha propuesto, drawText acepta cualquier CharSequence que significa que podemos hacer nuestra propia aplicación inteligente:

 final class PrefixedInt implements CharSequence { private final int prefixLen; private final StringBuilder buf; private int value; public PrefixedInt(String prefix) { this.prefixLen = prefix.length(); this.buf = new StringBuilder(prefix); } private boolean hasValue(){ return buf.length() > prefixLen; } public void setValue(int value){ if (hasValue() && this.value == value) return; // no change this.value = value; buf.setLength(prefixLen); buf.append(value); } // TODO: Implement all CharSequence methods (including // toString() for prudence) by delegating to buf } // Usage: private final PrefixedInt scoreText = new PrefixedInt("Your score is: "); ... scoreText.setValue(Score.points); canvas.drawText(scoreText, 0, scoreText.length(), x, y, paint); 

Ahora dibujar la puntuación no causa ninguna asignación (excepto quizás una o dos veces en el principio cuando el arreglo interno de buf puede tener que ser crecido, y cualquier drawText es hasta).

En una situación en la que es crítico evitar las pausas de GC, un truco que podrías usar es activar deliberadamente GC en un punto donde sabes que la pausa no importa. Por ejemplo, si se utiliza la función "showScores" que utiliza mucha basura al final de un juego, el usuario no se distraerá por un retraso de 200 ms entre mostrar la pantalla de puntuación y comenzar el siguiente juego … para que puedas llamar System.gc() una vez que se ha pintado la pantalla de puntuaciones.

Pero si recurre a este truco, debe tener cuidado de hacerlo sólo en los puntos donde la pausa del GC no será molesto. Y no lo haga si le preocupa drenar la batería del auricular.

Y no lo hagas en aplicaciones multiusuario o no interactivas, porque lo más probable es que la aplicación se ejecute más lento en general haciendo esto.

Respecto a la asignación de iteradores, evitar los iteradores en ArrayList`s es fácil. En lugar de

 for(GameObject gob : interactiveObjects) gob.onDraw(canvas); 

Solo puedes hacer

 for (int i = 0; i < interactiveObjects.size(); i++) { interactiveObjects.get(i).onDraw(); } 
  • ¿Por qué la aplicación diseñada por el material es más lenta que la aplicación diseñada por holo?
  • Desplazamiento rápido personalizado pulgar
  • Aumentar la velocidad de desarrollo de Android
  • RecyclerView vs ViewPager
  • ¿por qué mi aplicación Android es tan lenta?
  • Acelerar gradle build en la aplicación multidex
  • Cambiar el tamaño de las imágenes según la resolución de pantalla Android
  • ¿Cómo actualizar Android ListView con datos dinámicos en tiempo real?
  • El estudio de Android toma demasiada memoria
  • Android - ¿Cuál es más eficiente?
  • Compresión de imagen de Android sin carga completa en la memoria
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.