una sirve que para las importancia estructuras estructura ejemplos datos clasificacion caracteristicas java memory-management out-of-memory

java - sirve - importancia de la estructura de datos



¿Cómo estimar si la JVM tiene suficiente memoria libre para una estructura de datos en particular? (7)

Tengo la siguiente situación: hay un par de máquinas formando un clúster. Los clientes pueden cargar conjuntos de datos y tenemos que seleccionar el nodo en el que se cargará el conjunto de datos y rechazar cargar / evitar un error OOM si no hay una máquina que pueda ajustarse al conjunto de datos.

Qué hacemos actualmente: ahora contamos la entry count en el conjunto de datos y estimamos la memory to be used como entry count * empirical factor (determinado manualmente). Luego, compruebe si es inferior a la memoria libre ( Runtime.freeMemory() por Runtime.freeMemory() ) y, de ser así, cárguela (de lo contrario, vuelva a ejecutar el proceso en otros nodos / informe de que no hay capacidad libre).

Los problemas con este enfoque son:

  • el empirical factor necesita ser revisado y actualizado manualmente
  • freeMemory veces puede presentar un freeMemory debido a un poco de basura no limpiada (que podría evitarse ejecutando System.gc antes de cada llamada, sin embargo, eso ralentizaría el servidor y también podría conducir a una promoción prematura)
  • una alternativa sería "simplemente intente cargar el conjunto de datos" (y volver atrás si se lanza un OOM); sin embargo, una vez que se lanza un OOM, podría dañar otros hilos que se ejecutan en la misma JVM y no hay forma elegante de recuperarlo .

¿Hay mejores soluciones para este problema?


Un enfoque alternativo es aislar cada carga de datos en su propia JVM. Simplemente predefine el tamaño máximo de almacenamiento dinámico de cada JVM, y así sucesivamente, y establece el número de JVM por host de forma tal que cada JVM pueda ocupar su máximo tamaño de almacenamiento dinámico. Esto utilizará un poco más de recursos: significa que no puede utilizar cada byte de memoria acumulando más cargas de datos de poca memoria, pero simplifica enormemente el problema (y reduce el riesgo de equivocarse), hace posible saber cuándo / si necesita agregar nuevos hosts, y lo más importante, reduce el impacto que cualquier cliente puede tener en todos los demás clientes.

Con este enfoque, una JVM determinada está "ocupada" o "disponible".

Una vez completada una determinada carga de datos, la JVM pertinente puede declararse disponible para una nueva carga de datos o simplemente puede cerrarse. (De cualquier forma, querrá tener un proceso separado para monitorear las JVM y asegurarse de que el número correcto esté siempre en ejecución).


Los clientes pueden cargar conjuntos de datos y tenemos que seleccionar el nodo en el que se cargará el conjunto de datos y rechazar cargar / evitar un error OOM si no hay una máquina que pueda ajustarse al conjunto de datos.

Este es un problema de programación de trabajo, es decir , tengo recursos finitos, ¿cómo podemos utilizarlos mejor? Tendré el problema de OOM cerca del final.

Tenemos uno de los factores principales, es decir, RAM, pero las soluciones a los problemas de programación dependen de muchos factores, es decir ...

  1. Son los trabajos pequeños o grandes, es decir, hay cientos / miles de estos corriendo en un nodo o dos o tres. Piensa en el programador de Linux.

  2. ¿Tienen que completar en un marco de tiempo particular? Programador en tiempo real.

Teniendo en cuenta todo lo que sabemos al comienzo de un trabajo, ¿podemos predecir cuándo terminará un trabajo dentro de un período de tiempo determinado? Si podemos predecir que en el Nodo X liberamos 100MB cada 15-20 segundos, tenemos una manera de programar un trabajo de 200Mb en ese nodo, es decir, estoy seguro de que en 40 segundos habré completado 200Mb de espacio en ese nodo y los 40 segundos son un límite aceptable para la persona o la máquina que envía el trabajo.

Supongamos que tenemos una función de la siguiente manera.

predicted_time predict(long bytes[, factors]);

Los factors son las otras cosas que tendremos que tener en cuenta que mencioné anteriormente y para cada aplicación habrá cosas que puede agregar para adaptarlas a su situación.

Los factores se les da pesos cuando se calcula predicted_time .

predicted_time es la cantidad de milisegundos (puede ser cualquier TimeUnit) que este nodo crea desde ahora que puede dar servicio a esta Tarea, el nodo que le proporciona el número más pequeño es el nodo en el que debe programarse el trabajo. A continuación, puede usar esta función de la siguiente manera, donde tenemos una cola de tareas, es decir, en el siguiente código this.nodes[i] representa una instancia de JVM.

private void scheduleTask() { while(WorkEvent()) { while(!this.queue.isEmpty()) { Task t = this.queue.poll(); for (int i = 0; i < this.maxNodes; i++) { long predicted_time = this.nodes[i].predict(t); if (predicted_time < 0) { boolean b = this.queue.offer(t); assert(b); break; } if (predicted_time <= USER_EXPERIENCE_DELAY) { this.nodes[i].addTask(t); break; } alert_user(boolean b = this.queue.offer(t); assert(b); } } } }

Si predicted_time < 0 tenemos un error, reprogramamos el trabajo, en realidad nos gustaría saber por qué, pero eso no es difícil de agregar. Si el tiempo predicted_time <= USER_EXPERIENCE_DELAY el trabajo puede programarse.

Cómo evita esto un OOM

Podemos recopilar las estadísticas que queremos de nuestro programador, es decir, cuántos trabajos de tamaño X fueron programados correctamente, el objetivo sería reducir los errores y hacerlo más confiable con el tiempo, es decir, reducir la cantidad de veces que le decimos a un cliente que su trabajo no puede ser revisado Lo que hemos hecho es reducir el problema a algo que podamos mejorar estadísticamente hacia una solución óptima.


Los clientes pueden cargar conjuntos de datos y tenemos que seleccionar el nodo en el que se cargará el conjunto de datos y rechazar cargar / evitar un error OOM si no hay una máquina que pueda ajustarse al conjunto de datos.

Este es un problema de programación de trabajo, es decir , tengo recursos finitos, ¿cómo podemos utilizarlos mejor? Tendré el problema de OOM cerca del final.

Tenemos uno de los factores principales, es decir, RAM, pero las soluciones a los problemas de programación dependen de muchos factores, es decir ...

  1. Son los trabajos pequeños o grandes, es decir, hay cientos / miles de estos corriendo en un nodo o dos o tres. Piensa en el programador de Linux.

  2. ¿Tienen que completar en un marco de tiempo particular? Programador en tiempo real.

Teniendo en cuenta todo lo que sabemos al comienzo de un trabajo, ¿podemos predecir cuándo terminará un trabajo dentro de un período de tiempo? Si podemos predecir que en el Nodo X liberamos 100MB cada 15-20 segundos, tenemos una manera de programar un trabajo de 200Mb en ese nodo, es decir, estoy seguro de que en 40 segundos habré completado 200Mb de espacio en ese nodo y los 40 segundos son un límite aceptable para la persona o la máquina que envía el trabajo.

Supongamos que tenemos una función de la siguiente manera.

predicted_time predict(long bytes[, factors]);

Los factors son las otras cosas que necesitaríamos tener en cuenta que mencioné anteriormente y para cada aplicación habrá cosas que puede agregar para adaptarlas a su escenario, es decir, cuántos factores depende de usted.

Los factores se les da pesos cuando se calcula predicted_time .

predicted_time es la cantidad de milisegundos (puede ser cualquier TimeUnit) que este nodo crea desde ahora que puede dar servicio a esta Tarea, el nodo que le proporciona el número más pequeño es el nodo en el que debe programarse el trabajo. A continuación, puede usar esta función de la siguiente manera, donde tenemos una cola de tareas, es decir, en el siguiente código this.nodes[i] representa una instancia de JVM.

private void scheduleTask() { while(WorkEvent()) { while(!this.queue.isEmpty()) { Task t = this.queue.poll(); for (int i = 0; i < this.maxNodes; i++) { long predicted_time = this.nodes[i].predict(t); if (predicted_time < 0) { boolean b = this.queue.offer(t); assert(b); break; } if (predicted_time <= USER_EXPERIENCE_DELAY) { this.nodes[i].addTask(t); break; } alert_user(boolean b = this.queue.offer(t); assert(b); } } } }

Si predicted_time < 0 tenemos un error, reprogramamos el trabajo, en realidad nos gustaría saber por qué, pero eso no es difícil de agregar. Si el tiempo predicted_time <= USER_EXPERIENCE_DELAY el trabajo puede programarse.

Cómo evita esto un OOM

Podemos recopilar las estadísticas que queremos de nuestro programador, es decir, cuántos trabajos de tamaño X fueron programados correctamente, el objetivo sería reducir los errores y hacerlo más confiable con el tiempo, es decir, reducir la cantidad de veces que le decimos a un cliente que su trabajo no se puede reparar o falló. Lo que hemos hecho o al menos estamos tratando de intentar es reducir el problema a algo que podamos mejorar estadísticamente para lograr una solución óptima.


una alternativa sería "simplemente intente cargar el conjunto de datos" (y volver atrás si se lanza un OOM); sin embargo, una vez que se lanza un OOM, podría dañar otros hilos que se ejecutan en la misma JVM y no hay forma elegante de recuperarlo .

No hay buenas maneras de manejar y recuperarse de OOME en JVM, pero hay forma de reaccionar antes de que ocurra OOM. Java tiene java.lang.ref.SoftReference que se garantiza que se borró antes de que la máquina virtual arroje OutOfMemoryError . Este hecho se puede utilizar para la predicción temprana de OOM. Por ejemplo, la carga de datos puede anularse si se activa la predicción.

ReferenceQueue<Object> q = new ReferenceQueue<>(); SoftReference<Object> reference = new SoftReference<>(new Object(), q); q.remove(); // reference removed - stop data load immediately

La sensibilidad se puede sintonizar con -XX: Indicador SoftRefLRUPolicyMSPerMB (para Oracle JVM). Solución no ideal, su efectividad depende de varios factores: haga otras referencias suaves usadas en el código, cómo está afinado el GC, la versión JVM, el clima en Marte ... Pero puede ayudar si tiene suerte.


El empirical factor puede calcularse como un paso de compilación y colocarse en un archivo de propiedades.

Mientras freeMemory() es casi siempre menor que la cantidad que sería gratis después de un GC, puede verificarlo para ver si está disponible y llamar a System.gc() si maxMemory() indica que puede haber suficiente.

NOTA: Usar System.gc() en producción solo lo hace en situaciones muy raras y, en general, a menudo se usa incorrectamente, lo que resulta en una reducción del rendimiento y oscurece el problema real.

Evitaría activar un OOME a menos que esté ejecutando una JVM. Puede reiniciar según sea necesario.


Como ha señalado con razón, el uso de freeMemory no le indicará la cantidad de memoria que Java Garbage Collection puede liberar. Podría ejecutar pruebas de carga y comprender el patrón de uso de almacenamiento JVM y la asignación de memoria, el patrón de desasignación utilizando herramientas como la opción JConsole, VisualVM, jstat e printGCStats en JVM. Esto dará una idea sobre el cálculo del empirical factor mayor precisión, básicamente comprenderá cuál es el patrón de carga que su aplicación Java puede manejar. Lo siguiente sería elegir el GC correcto y sintonizar la configuración básica del GC para una mejor eficiencia. Esta no es una solución rápida, pero quizás a largo plazo sea una mejor solución.

La otra forma de matar su JVM con -XX: OnOutOfMemoryError = "kill -9% p" configuración de JVM, una vez que OOM sucede, y luego escribe, resuene un script de monitorización de proceso simple para que aparezca su JVM si está inactivo.


Mi solución:

  1. Establezca Xmx como 90%-95% de RAM de la máquina física si no se está ejecutando ningún otro proceso, excepto su programa. Para una máquina RAM de 32 GB, configure Xmx como 27MB - 28MB .

  2. Use uno de los buenos algoritmos de gc: CMS o G1GC y ajuste los parámetros relevantes. I prefer G1GC if you need more than 4 GB RAM for your application . Consulte esta pregunta si elige G1GC:

    Estrategia agresiva de recolectores de basura

    Reducir el tiempo de pausa de JVM> 1 segundo con UseConcMarkSweepGC

  3. Calcule Cap en el uso de memoria por usted mismo en lugar de verificar la memoria libre. Agregue memoria usada y memoria para ser asignada. Subtract it from your own cap like 90% of Xmx . Si todavía tiene memoria disponible, otorgue la solicitud de asignación de memoria.