Estoy desarrollando una aplicación en Java que se ejecuta en dispositivos con Windows Mobile. Para lograr esto, hemos utilizado el Esmertec JBed JVM, que no es perfecto, pero por el momento estamos atascados. Recientemente hemos recibido quejas de clientes sobre OutOfMemoryErrors. Después de jugar un montón de cosas, descubrí que el dispositivo tiene mucha memoria libre (aproximadamente 4 MB).Evite la fragmentación de la memoria al asignar muchas matrices en Java
Los OutOfMemoryErrors siempre se producen en el mismo punto del código y es cuando se expande un StringBuffer para agregarle algunos caracteres. Después de agregar algo de registro en esta área, encontré que mi StringBuffer tenía aproximadamente 290000 caracteres con una capacidad de aproximadamente 290500. La estrategia de expansión de la matriz de caracteres internos es simplemente duplicar el tamaño, por lo que estaría intentando asignar una matriz de alrededor de 580000 caracteres. Imprimí el uso de la memoria también esta vez y encontré que estaba usando aproximadamente 3.8MB de aproximadamente 6.8MB en total (aunque he visto que la memoria total disponible aumenta a alrededor de 12MB a veces, por lo que hay mucho espacio para la expansión). Entonces es en este punto que la aplicación informa un OutOfMemoryError, que no tiene mucho sentido dado cuánto todavía hay libre.
Empecé a pensar en el funcionamiento de la aplicación hasta este punto. Básicamente lo que está sucediendo es que estoy analizando un archivo XML usando MinML (un pequeño analizador de Saxo XML). Uno de los campos en el XML tiene aproximadamente 300k caracteres. El analizador transmite los datos del disco y, de forma predeterminada, solo carga 256 caracteres a la vez. Entonces, cuando llegue al campo en cuestión, el analizador llamará al método 'caracteres()' del manejador más de 1000 veces. Cada vez creará un nuevo carácter [] con 256 caracteres. El controlador simplemente agrega estos caracteres a un StringBuffer. El tamaño inicial predeterminado de StringBuffer es solo 12, de modo que cuando los caracteres se añaden al búfer, tendrá que crecer varias veces (cada vez creando un nuevo carácter []).
Mi suposición a partir de esto fue que, si bien hay suficiente memoria libre ya que los anteriores [] pueden ser basura, tal vez no haya un bloque de memoria contiguo lo suficientemente grande como para adaptarse a la nueva matriz que estoy tratando de asignar. Y tal vez la JVM no es lo suficientemente inteligente como para expandir el tamaño del montón porque es estúpido y piensa que no es necesario porque aparentemente hay suficiente memoria libre.
Así que mi pregunta es: ¿Alguien tiene alguna experiencia con esta JVM y podría confirmar o desmentir de manera concluyente mis suposiciones acerca de la asignación de memoria? Y también, ¿alguien tiene alguna idea (suponiendo que mis suposiciones sean correctas) sobre cómo mejorar la asignación de las matrices para que la memoria no se fragmente?
Nota: Las cosas que he intentado ya:
- que aumentó el tamaño de la matriz inicial de la StringBuffer y yo increaed el tamaño de lectura del analizador de manera que no tenga que crear tantas matrices.
- Cambié la estrategia de expansión de StringBuffer de modo que una vez que alcanzara un cierto umbral de tamaño, solo se expandiría en un 25% en lugar de un 100%.
Hacer ambas cosas ayudó un poco, pero a medida que aumente el tamaño de los datos xml, todavía obtengo OutOfMemoryErrors en un tamaño bastante bajo (aproximadamente 350kb).
Otra cosa que se debe agregar: todas estas pruebas se realizaron en un dispositivo utilizando la JVM en cuestión. Si ejecuto el mismo código en el escritorio usando Java SE 1.2 JVM, no tengo ningún problema, o al menos no entiendo el problema hasta que mis datos alcancen los 4MB de tamaño.
EDIT:
otra cosa He intentado lo que ha ayudado es un poco me puse la XMS a 10M. Así que esto supera el problema de que la JVM no expande el montón cuando debería y me permite procesar más datos antes de que ocurra el error.
¿Estás seguro? Ese artículo habla sobre cómo hacer que los objetos * sean más fáciles * para recolectar basura. –
No estoy creando ningún objeto de referencia? Como dije, no creo que tenga un problema con los objetos que no se recogen porque la JVM informa suficiente memoria libre. Es una cuestión de dónde está la memoria libre? ¿Está fragmentado? ¿Es por eso que la JVM no puede asignar mi nueva matriz? – DaveJohnston