Siguiendo con la serie que comenzamos sobre J2ME en la práctica de cada día, vamos a dedicar este artículo a otro aspecto peliagudo de la programación con móviles: la memoria y sus problemas ;-)
Primero vamos a describir lo que os contarán los tutoriales sobre la memoria de los móviles y su gestión, y luego vendrá la sorpresa final por cortesía de Linking Paths ;-)
LA MEMORIA Y COMO KVM LA GESTIONA
Como referencia os aconsejo un artículo estupendo sobre el tema: aquí. Lo resumiremos para los hispano hablantes y los perezosos ;-)
Como en sus primos mayores, en J2ME el encargado de gestionar la memoria es la máquina virtual, KVM en los móviles. Cuando se inicia la máquina, KVM reserva un bloque de la memoria volátil del móvil como espacio de heap: es allí donde se cargarán las clases que componen nuestra aplicación y donde almacenarán el valor de los datos no persistentes (en el siguiente artículo de la serie hablaremos sobre los datos persistentes y la RMS).
Por supuesto, por las limitaciones típicas del hardware el valor máximo de la memoria es muy reducido, pero que muy reducido. Cualquier programador java acostumbrado a programar aplicaciones J2EE o J2SE, no es que se preocupe muchas veces por conocer el tamaño en Kilobytes de sus clases, pero en cuanto se adentra en j2ME se encuentra con que puede llegar a tener una memoria disponible inferior incluso en algunos modelos a los 200K. Además esa memoria es la disponible muchas veces también para determinadas aplicaciones de sistema del móvil, así que aún tendremos menos para nuestra aplicación.
El algoritmo que utiliza la KVM para liberar la memoria y reservar memoria para los objetos es muy simple: marcar y limpiar. En cuanto un objeto deja de utilizarse, y es candidato para el recolector de basura, su porción de memoria se marca como libre y puede ser utilizada por la KVM para colocar otro objeto. El problema de este algoritmo es que no relaciona espacios de memoria libres, aunque sean adyacentes, y por lo tanto produce una gran fragmentación de la memoria.
En el peor de los casos, se puede producir la situación de que aunque tengamos espacio libre, esté tan fragmentado que no podamos crear objetos nuevos. En honor a loa verdad, puedo deciros que en la aplicación en la que trabajamos existe una gran creación de objetos, y que en ningún momento hemos llegado a tener ese problema, pero teóricamente se puede dar y habrá que tenerlo en cuenta el día en el que sorprendentemente nuestra aplicación comience a soltar excepciones de memoria.
El algoritmo que utiliza KVM crea una gran fragmentación de memoria, los bloques de memoria que hayan sido reservados alguna vez no son integrados con otros bloques para lograr espacios disponibles mayores
Una solución se presenta en el artículo mencionado, que propone utilizar pools de objetos. En nuestra experiencia no ha sido necesario, pero ahí queda para el que lo quiera implementar.
COMIENZAN LAS SORPRESAS: TAMAÑOS MÁXIMOS DE JARS
Pues un programador después de leer lo anterior, se queda tan ufano, y como el móvil en el que va a trabajar tiene un heap de 250K, crea un bonito programa, que solamente ocupa 101K, y se dispone a probarlo en el móvil. OUCH!!!! No se puede instalar!!!
Pues si, no se puede instalar porque por mucha memoria heap que presente un teléfono, absolutamente todos los teléfonos presentan otra limitación, que es dependiente del modelo: el tamaño máximo del Jar. No se podrán instalar aplicaciones en ese teléfono cuyo tamaño de jar sea superior a ese valor, y ese valor casi siempre es mucho más pequeño que el tamaño de la memoria de heap.
La razón, según nuestras investigaciones, puede tener más que ver con temas de la persistencia y las limitaciones de la RMS (de las que hablaremos en otro post), pero es una limitación que puede mandar al traste muchos proyectos. Un buen sitio para consultar esos tamaños máximos es la base de datos de dispositivos en j2mepolish.
TODOS los teléfonos limitan el tamaño máximo permitido para un jar, y ese valor suele ser siempre menor que la memoria disponible
La solución consiste en utilizar un buen ofuscador (como por ejemplo proguard) que disminuya el tamaño de nuestro jar. Lo que si que hemos descubierto es que los ofuscadores comerciales consiguen disminuir el tamaño del jar muchísimo más (y hablamos de diferencias de incluso 25K), a costa del precio que tienen (que no es bajo ;-)).
NO SE VAYAN TODAVÍA, AÚN HAY MÁS: TAMAÑO MÁXIMO DE OBJETO
Y por fin, la sorpresa final: es algo que no he visto comentar demasiado, y que incluso en la base de datos de j2mepolish se pasa de soslayo, pero que nosotros hemos encontrado en TODOS LOS MÓVILES, MARCAS Y MODELOS (y os puedo asegurar que hacer una aplicación que “debe” funcionar en 300 modelos de móviles te da un amplio espectro de muestra ;-)). Por lo cual me extraña que no haya más referencias en Internet (ahora habrá una ;-))
Aunque la memoria no esté fragmentada, el jar se haya podido instalar, existe otra limitación de memoria: el tamaño máximo del objeto que se puede crear. Aunque haya espacio de memoria, los móviles no dejan crear un objeto con un tamaño mayor a cierto valor (por supuesto, dependiente de cada dispositivo, ¿a alguien le suena WORA? ;-))
Un código sencillo para testear esto puede ser un simple bucle como el mostrado:
_memory = new StringBuffer();
int size = 0;
try {
while(active){
_memory.append(trashData);
size += 1024;
}
} catch (Throwable e) {
objectMaxSize.setText(String.valueOf(size/1024) + " K");
}
trashData es un simple “Lorem Ipsum…” de 1K ( http://www.lipsum.com/ para generarlo) y objectMaxSize, un StringItem para mostrarlo por pantalla. El StringBuffer irá creciendo hasta que salte la excepción. Lllamando a runtime.freeMemory() podéis comprobar como la excepción salta muchísimo antes de que se llegue a llenar toda la memoria.
TODOS los teléfonos móviles limitan el tamaño máximo permitido para un objeto en memoria, y es un valor independiente de la memoria libre disponible o lo fragmentada que esté
Es curioso porque en la base de datos de j2mepolish, sólamente se indica este parámetro para los móviles Siemens (propiedad polish.maxobjectsize, por ejemplo: http://www.j2mepolish.org/devices/Siemens/2128.html, heapsize=150K, maxObjectSize=16K, verdad, que j2me es “divertido”?), pero no lo suelen indicar para otras marcas. ¿Falta de datos?¿No les gustan los Siemens? ;-). Y os podemos asegurar que pasa en todas las marcas y modelos ;-D
Pues nada, ya vale de ladrillo, el siguiente sobre RMS y prometemos alguna sorpresa también ;-)


Me parecio super bueno el articulo ya habia tenido problemas al montar una aplicacion j2me en los celulares y la verdad me tuvo pensando bastante el porque me salia un error lo bueno es que al final pude descubrir que era :-). Me gustaria que me ayudaras con un problemita que tengo al almacenar en el rms, no se porque no puedo sino escribir un maximo de registros y no me deja avanzar de alli lo que me puedas contar al respecto seria muy util gracias
En cuanto saue algo de tiempo escribo el artículo dedicado a RMS. Mientras tanto un par de pistas:
- El tamaño máximo libre que devuelven los métodos es mayor que le real existente.
- EXiste un número máximo de registros dependiendo del tamaño de ellos.
Salu2
Buenisimo el artículo.
Me vino bien para tener noción del V3 en el que experimento -jeje- ya que mi programa crece y crece en memoria hasta que revienta…
Madre mia, esto si q lo habia comprobado personalmente -al menos lo de los objetos en memoria y la aplicacion en memoria, lo de los jars a sido un bonito añadido de indefension- Q horror….
Hola
Esta bueno el articulo.
Sabes ahora estoy experimentando problemas con mi aplicación y la máquina virtual, bueno trabajo con un equipo Palm Tungsten E2 pero últimamente estoy recibiendo un “Fatal Error” no me habia sucedido antes.. pero ahora tengo el problema desde que cargo la VM de IBM en mi equipo.. tal vez has experimentado un error de este tipo que me des algunas pistas.. por cierto ya he realizado un hard-reset al equipo pero no se soluciona.
Gracias..
Saludos.