-
Ahora que todo nuestro código se ejecuta
rápido y genial, hablemos
-
un poco más acerca de la memoria y cómo
afecta el rendimiento en nuestro sistema.
-
Muchos lenguajes de programación
conocidos por ser cercanos al hardware,
-
o más bien, de alto desempeño, como
C, C++
-
y fortran, los programadores suelen tener
que administrar la memoria ellos mismos.
-
Los programadores son los responsables de
-
asignar un bloque de memoria y,
posteriormente, desasignarlo
-
cuando han terminado de utilizarlo.
-
dado que uno mismo define cuando y
cuánta memoria asignar, toda la
-
calidad de la administración de memoria
depende de tus habilidades y efectividad.
-
Es mucha responsabilidad.
-
Y la realidad es que los programadores
no siempre son los mejores en controlar
-
todos esos trocitos de memoria.
-
Piénsalo, el desarrollo de
productos es laborioso y
-
agitado, y muchas veces la memoria acaba
no siendo liberada correctamente.
-
Esos bloques de memoria sin liberar son
llamados fugas de memoria, y andan
-
por ahí acaparando recursos, que podrías
estar utilizando en otra parte.
-
Para reducir éste caos, estrés, e incluso
grandes pérdidas monetarias,
-
causadas por fugas de memoria, se crearon
los lenguajes de memoria administrada.
-
Los tiempos de ejecución de éstos
lenguajes rastrean las asignaciones y
-
liberan memoria al sistema cuando ya no
hace falta a la
-
aplicación en sí misma. Sin ninguna
intervención por parte del programador.
-
Éste arte, o más bien ciencia, de
recuperar memoria en un ambiente
-
de memoria administrada, se conoce como
"recolección de basura", el concepto fue
-
creado en 1959 por John McCarthy para
resolver problemas en el lenguaje LISP.
-
Los principios básicos de recolección de
basura son: número uno,
-
encontrar objetos de datos en un programa
al que no se puede acceder en el futuro,
-
por ejemplo, cualquier memoria que ya
no esté referida por el código.
-
Y número dos, recuperar los recursos
utilizados por dichos objetos.
-
El concepto es simple en teoría,
pero bastante complejo cuando
-
tienes 2 millones de lineas de código, y
cuatro Gigas de Asignaciones.
-
Piénsalo, la recolección de basura puede
ser realmente irritante,
-
si tienes unas 20.000 asignaciones en
tu programa ahora mismo.
-
Cuáles ya no se necesitan?
-
O mejor, ¿cuándo debes ejecutar un evento
de recolección de basura
-
para liberar memoria sin utilizar?
-
Éstas preguntas son realmente difíciles y
-
afortunadamente, hemos tenido unos 50
años de innovación para mejorarlas.
-
por eso el recolector de basura en
el tiempo de ejecución de Android
-
es bastante más sofisticado que la
propuesta original de McCarthy.
-
Ha sido creado para ser tan rápido y
no invasivo como sea posible.
-
En efecto, las pilas de memoria en los
tiempos de ejecución de Android
-
están segmentadas en espacios
-
basado en el tipo de asignación y
-
en cómo el sistema puede organizar mejor
las asignaciones para futuros eventos GC.
-
Cuando se asigna un nuevo objeto,
-
éstas características son tomadas en
cuenta para ajustarse a los espacios
-
más adecuados, dependiendo de la versión
del tiempo de ejecución Android que uses.
-
Y aquí viene lo importante:
-
Cada espacio tiene un tamaño específico.
-
A medida que se asignan objetos, se van
vigilando los tamaños combinados, y
-
cuando el tamaño empieza a crecer, el
sistema tendrá que ejecutar un evento
-
de recolección de basura, intentando
liberar memoria para futuras asignaciones.
-
Vale destacar que los eventos de GC
se comportan de forma diferente
-
dependiendo de cuál tiempo de
ejecución de Android se utilice.
-
Por ejemplo, en Dalvik, muchos eventos
de GC detienen los eventos mundiales,
-
así, cualquier código administrado
que se esté ejecutando se detiene hasta
-
completar la operación
-
Lo que genera problemas cuando los GC
tardan más de lo normal, o cuando
-
ocurren muchos al mismo tiempo.
-
dado que van a consumir parte
significativa de tu tiempo.
-
Y, seamos claros,
-
los ingenieros de Android han invertido
mucho tiempo en asegurarse de que éstos
-
eventos sean lo más rápidos posible para
evitar interrupciones. Dicho esto,
-
pueden causar problemas en el
desempeño de aplicaciones en tu código.
-
En primer lugar, entiende que, mientras
más tiempo pase tu app haciendo GC en
-
cierto marco, menos tiempo tendrá para el
resto de la lógica que necesita para
-
mantenerte bajo la barrera de
Renderización de 16 milisegundos
-
Asi que, si tienes un montón de GC, o
algunos muy largos que ocurren uno
-
tras otro, eso podría ubicar tu tiempo de
procesamiento de cuadros por sobre los 16
-
milisegundos
-
que es la barrera de renderización, lo
que causaría ralentización o fallas a
-
tus usuarios.
-
En segundo lugar, entiende que el flujo
del código pudiera estar haciendo cosas
-
que obligan a eventos GC más frecuentes
o más largos de lo normal.
-
Por ejemplo, si estás asignando un montón
de objetos en la parte interior
-
de un lazo que se ejecuta durante largo
tiempo, contaminarás tu
-
pila de memoria con muchos objetos
y acabarás generando muchos GC
-
rápidamente, debido a ésta
presión adicional sobre la memoria.
-
E incluso considerando que estamos en
un ambiente de memoria administrada,
-
pueden ocurrir fugas de memoria.
-
Aunque no es tan fácil crearlas como
en otros lenguajes.
-
Éstas fugas pueden contaminar tu pila
con objetos que no se liberan
-
durante un evento GC, reduciendo
efectivamente el espacio utilizable y
-
obligando a que ocurran más eventos GC
con regularidad.
-
Así que eso es,
-
si quieres reducir la cantidad de GC que
ocurren en un cierto plazo,
-
debes enfocarte en optimizar el
uso de memoria de las apps.
-
Desde una perspectiva de código, puede
ser difícil detectar de dónde
-
salen éstos problemas, pero
-
afortunadamente, el Android SDK pone
un juego de poderosas herramientas
-
a tu disposición.
-
Veamos.