Cuerdas y gestión de memoria
Me cuesta mucho entender lo que está pasando con las cadenas y la gestión de la memoria en Android (Java).
Para simplificar, eche un vistazo a esta sencilla pieza de código:
- Cuando Android ofrece Excepción OutOfMemory?
- Android sin memoria al asignar variables
- ¿Cuánto cuesta el receptor de radiodifusión por la memoria?
- ¿Cómo elegir el tamaño de imagen óptimo para no exceder el presupuesto de VM?
- Android - es onDestroy supone destruir la actividad, sus variables y liberar memoria
public void createArray(){ String s = ""; String foo = "foo"; String lorem = "Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an."; ArrayList<Item> array = new ArrayList<Item>(); for( int i=0; i<20000; i++ ){ s = foo.replace("foo", lorem); array.add( new Item(s) ); } System.gc(); } private class Item{ String name; public Item(String name){ this.name = name; } }
Si se ejecuta, createArray
asignará más de 5Mb en la memoria. Incluso con la instrucción System.gc()
, la memoria no se desasignará después del bucle.
Ahora, si reemplazamos s = foo.replace("foo", lorem);
Con s = lorem;
, La memoria asignada sólo aumentará en 0.5Mb.
Necesito entender lo que está pasando para mejorar el rendimiento de mi aplicación.
¿Puede alguien explicar cómo debo reemplazar las cuerdas en un caso como este? ¿Y por qué System.gc()
no desasigna la memoria?
Gracias.
ACTUALIZACIÓN :
Gracias por las respuestas, ahora entiendo que System.gc()
es sólo una pista.
Para aclarar la otra pregunta (la importante):
¿Cómo puedo generar dinámicamente 20000 cadenas ("foo1", "foo2" … "foo20000") agregarlos a la ArrayList y no quedan sin memoria? Si estas 20000 cadenas eran estáticas no asignarían más de 0.5Mb en memoria. Además, si s = foo.replace("foo", lorem)
crea una cadena nueva, ¿por qué mi función asigna 5Mb que es 10 veces más memoria? ¿No debería ser alrededor de 1Mb?
(Ya estoy pensando en una solución, pero quiero estar seguro de que no hay manera de generar las cadenas dinámicamente sin utilizar esta enorme cantidad de memoria)
- Java - ¿Tendrá beneficios el código inlining?
- Android Eclipse DDMS> Heap> Causa gris de GC
- Android: eliminar / destruir objetos cuando se gira la pantalla
- Android: libera recursos de memoria de mapa de bits de forma programática
- ¿Cómo entender que muestra LeakCanary?
- eliminar vistas que no son visibles en gridView
- Error de memoria de mapa de bits - Android
- Creación de una carpeta en la memoria interna para guardar archivos y recuperarlos más tarde
replace
está generando un nuevo objeto String
cada vez que se llama, ya que las cadenas en Java son inmutables y por lo tanto las modificaciones no se pueden hacer "ìn-place". Además, agrega esa String
a una ArrayList
que contendrá una referencia al nuevo objeto, evitando que se recopile.
Debido a que String
es inmutable, llamar a foo.replace("foo", lorem)
creará una nueva String
cada vez. En el ejemplo en el que simplemente establece s = lorem
, no se crea ninguna nueva String
.
Además, System.gc () es simplemente una recomendación a la VM, y de ninguna manera garantiza una recolección de basura. No hay nada que puedas hacer para forzar una recolección de basura. (Aparte de usar toda la memoria disponible que sea)
System.gc () es una pista para que el recolector de basura ejecute de nuevo, cuando tiene el tiempo.
Dicho esto, el recolector de basura sólo recoge objetos no referenciados, y todas las cadenas que generó todavía están siendo retenidas por la array
objetos. Es totalmente posible que usted puede agregar las líneas después de System.gc()
for (String item : array) { System.out.println(item); }
Lo que habría demostrado la sabiduría del recolector de basura en no destruir las cuerdas.
En el caso de que tenga alguna verdadera necesidad de reutilizar una cadena, puede intercambiar ciclos de CPU para una huella de memoria posiblemente menor con los métodos internos de cadena (que escanearán cadenas asignadas para duplicados y devolverán una referencia a la única cadena guardada si se encuentra.
Podría argumentar que en su caso la sugerencia de recolector de basura podría invocar técnicas de optimización de tiempo de compilación para darse cuenta de que la matriz no se está utilizando en sentido descendente y, por tanto, actúa para destruir las cadenas antes de que normalmente se desreferenciaran al final del programa; Sin embargo, agregaría efectos secundarios que parecerían bastante extraños en algunos entornos de ejecución (como sesiones de depuración).
Al final, no es posible realizar con precisión la reflexión, el paso de código, compilar la vinculación al código fuente y varios tipos de reflexión con tal optimización exótica de tiempo de compilación. Dicho esto, Java se optimiza bastante bien teniendo en cuenta cuántas características obtienes fuera de la caja.
Tenga en cuenta que System.gc()
no obliga al recolector de basura a ejecutarse. Es simplemente una pista que le gustaría que se ejecute en algún momento en el futuro.
- Proguard falla con la versión 25.1.6 de las herramientas de Android
- Menú emergente con icono en Android