management collector java android garbage-collection performance

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



android memory management (6)

Aunque esta es una pregunta de hace 2 años ...

El único, y el mejor método para evitar el retraso del GC es evitar el GC al asignar todos los objetos requeridos de manera estática (incluso al inicio). Precrear todo el objeto requerido, y nunca hacer que se eliminen. Use la agrupación de objetos para reutilizar el objeto existente.

De todos modos, es posible que tenga una pausa final incluso después de haber hecho todas las optimizaciones posibles en su código. Porque cualquier cosa que no sea el código de tu aplicación sigue creando objetos GC internamente que eventualmente se convertirán en basura. Por ejemplo, biblioteca base de Java . Incluso el uso de una simple clase List puede crear basuras. (así que debe evitarse) Llamar a cualquier API de Java puede crear basuras. Y estas asignaciones no son evitables mientras usa Java.

Además, como Java está diseñado para utilizar GC, tendrá problemas por falta de funciones si realmente trata de evitar el GC. (incluso la clase List debe evitarse). Debido a que permite el GC, todas las bibliotecas pueden usar GC, por lo que virtualmente / prácticamente no tiene biblioteca . Considero que evitar GC en el lenguaje basado en GC es una especie de prueba loca.

En definitiva, la única forma práctica es bajar al nivel inferior donde puedes controlar la memoria por completo. Tales como los lenguajes de la familia C (C, C ++, etc.). Entonces ve a NDK.

Nota

Ahora Google está enviando GC incremental (concurrente?) Que puede disminuir la pausa mucho. De todos modos, GC incremental significa simplemente distribuir la carga del GC a lo largo del tiempo, por lo que aún verá una pausa eventual si la distribución no es ideal. Además, el rendimiento del GC se reducirá debido al efecto secundario de una menor sobrecarga de operación de lotes y distribución.

Estoy optimizando el rendimiento de los juegos interactivos en Java para la plataforma Android. De vez en cuando hay un tropiezo 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 200 ms en dispositivos muy lentos.

Estoy usando el generador de perfiles ddms (parte del SDK de Android) para buscar de dónde provienen las asignaciones de memoria y eliminarlas de mi dibujo interno y de los bucles lógicos.

El peor ofensor había sido bucles cortos hechos como,

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

donde cada vez que se ejecutaba el bucle había un iterator asignado. Estoy usando matrices ( ArrayList ) para mis objetos ahora. Si alguna vez quiero árboles o hashes en un ciclo interno, sé que debo tener cuidado o incluso volver a implementarlos en lugar de utilizar el marco de colecciones Java, ya que no puedo pagar la recolección de basura adicional. Eso puede aparecer cuando estoy buscando colas de prioridad.

También tengo problemas para mostrar las puntuaciones y el progreso utilizando Canvas.drawText . Esto es malo,

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

porque Strings , char arrays y StringBuffers se asignarán para que funcionen. Si tiene algunos elementos de visualización de texto y ejecuta el marco 60 veces por segundo, esto comienza a acumularse y aumentará los inconvenientes de la recolección de basura. Creo que la mejor opción aquí es mantener matrices char[] y decodificar manualmente su int o double en él y concatenar cadenas al principio y al final. Me gustaría saber si hay algo más limpio.

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


Con respecto a la asignación del iterador, evitar 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(); }


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

Úselo así:

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 un entero a una cadena de forma manual (dígito por dígito) para eliminar toda la basura.

Además de eso, uso SparseArray siempre que sea posible, porque todas las estructuras de datos de Java, como HashMap , ArrayList , etc. tienen que usar el boxeo para tratar con tipos primitivos. Cada vez que inserta un int en Integer , este objeto Integer debe ser limpiado por el GC.


En una situación en la que es esencial evitar las pausas de GC, un truco que podría usar es activar deliberadamente GC en un punto en el que sepa que la pausa no es importante. Por ejemplo, si la función intensiva de basura "showScores" se usa al final del juego, el usuario no se distraerá demasiado con un retraso adicional de 200ms entre la visualización de la pantalla de puntaje y el inicio del próximo juego ... para que pueda llamar System.gc() una vez que la pantalla de puntajes ha sido pintada.

Pero si recurre a este truco, debe tener cuidado de hacerlo solo en los puntos donde la pausa del GC no sea molesta. Y no lo haga si le preocupa agotar la batería del teléfono.

Y no lo haga en aplicaciones multiusuario o no interactivas porque lo más probable es que haga que la aplicación funcione más lentamente al hacerlo.


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

No hay una manera "limpia" de lidiar con esto y primero daré un ejemplo ...

Normalmente tiene, por ejemplo, 4 bolas en pantalla a (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 }

"Pop" la segunda bola que desaparece, tu int [] se convierte en:

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

(fíjate que ni siquiera nos importa "limpiar" la 4ª bola (98,73), simplemente hacemos un seguimiento del número de bolas que nos quedan).

Seguimiento manual de objetos, por desgracia. Así es como se hace en la mayoría de los juegos de Java que funcionan bien y que están en dispositivos móviles.

Ahora, para cuerdas, esto es lo que haría:

  • al inicio del juego, predibuje usando drawText (...) solo una vez los números del 0 al 9 que guarde en una matriz BufferedImage[10] .
  • al inicio del juego, predrague una vez "Su puntaje es:"
  • si el "Su puntaje es:" realmente necesita volver a dibujarse (porque, por ejemplo, es transparente), luego vuelva a dibujarlo desde su imagen almacenada previamente almacenada
  • para calcular los dígitos del puntaje y agregar, después de "Su puntaje es:" , cada dígito manualmente uno por uno (copiando cada vez el dígito correspondiente (0 a 9) de su BufferedImage[10] donde preparó los almacenó.

Esto te da lo mejor de ambos: obtienes la reutilización de la fuente drawtext (...) y creaste exactamente cero objetos durante tu ciclo principal (porque también esquivaste la llamada al drawtext (...) que a su vez puede ser crappily generando, bueno, basura innecesaria).

Otro "beneficio" de este "puntaje de sorteo de creación de objetos cero" es que el almacenamiento en memoria caché y la reutilización cuidadosa de las fuentes no es realmente "asignación / desasignación manual de objetos" , sino que se trata de un almacenamiento en caché muy cuidadoso.

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

Y es rápido. Maldito rápido Más rápido que cualquier cosa que implique la creación de un objeto.

PD: En realidad, si observas cuidadosamente algunos juegos para dispositivos móviles, notarás que a menudo las fuentes no son fuentes de sistema / Java sino fuentes perfectas para cada juego (aquí acabo de darte un ejemplo de cómo almacenar en caché el sistema) / Fuente de Java, pero obviamente también podría almacenar en caché / reutilizar una fuente perfecta de píxeles / mapa de bits).


Si no desea preprocesar el texto como se ha propuesto, drawText acepta cualquier CharSequence que significa que podemos hacer nuestra propia implementación inteligente de la misma:

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 el puntaje no causa ninguna asignación (excepto tal vez una o dos veces al principio, cuando la matriz interna del buf puede tener que crecer, y cualquier drawText es hasta).