Cada vez que tu aplicación crea un subproceso nuevo, el sistema operativo debe asignar memoria para admitirlo. Si bien los subprocesos se consideran "livianos" en comparación con los procesos completos, su costo de memoria no es cero. La fuga de subprocesos es una forma muy común de fuga de memoria en Android.
El costo de un subproceso
Cuando se crea un subproceso en Android, dos componentes principales consumen memoria:
- La pila nativa: La biblioteca
pthreadasigna una pila para que el subproceso almacene variables locales y marcos de llamadas. En Android, suele ser de 1 MB de forma predeterminada. - Estructuras internas de JVM/kernel: ART y el kernel de Linux deben asignar estructuras de datos (como objetos
pthread_internal_tyjava.lang.Thread) para hacer un seguimiento del subproceso y administrarlo.
Memoria de pila y mmap
Es importante comprender que Android asigna la pila nativa de 1 MB con mmap con las marcas MAP_ANONYMOUS | MAP_NORESERVE.
Esto significa que el SO reserva 1 MB de espacio de direcciones de memoria virtual, pero no asigna 1 MB de RAM física de inmediato. La memoria física solo se asigna (se pagina) a medida que el subproceso ejecuta código y envía datos a la pila. Un subproceso que no hace nada consumirá muy poca RAM física para su pila.
Ejercicio práctico: Subprocesos con fugas
Usaremos la aplicación de ejemplo MemoryLab para observar lo que sucede cuando una aplicación crea cientos de subprocesos inactivos.
1. Lanzamiento y modelo de referencia
Inicia la app y toma una lectura de memoria de referencia:
adb shell am start -W -n com.android.memorylab/.MainActivity
adb shell dumpsys meminfo -s com.android.memorylab
Observa las filas Stack y Native Heap en el resultado. Es probable que sean
pequeños.
2. Crea 100 hilos
Presiona el botón Create 100 Threads. La aplicación generará 100 subprocesos nuevos. Para garantizar que la memoria de pila se pagine en la RAM física (para que podamos verla en nuestras herramientas), cada subproceso asigna y escribe en un pequeño array de 10 KB en su pila antes de comenzar a esperar.
Vuelve a ejecutar el comando meminfo mientras los subprocesos estén activos:
adb shell dumpsys meminfo -s com.android.memorylab
Los resultados:
Compara el resultado nuevo con el de referencia. Notarás aumentos significativos mientras los subprocesos estén activos:
Stack: Aumentó un megabyte o más. Esto representa las páginas físicas asignadas para las 100 pilas de subprocesos (cada una de las cuales toca al menos 10 KB).Native Heap/Private Other: Aumenta debido a las estructuras internas depthreadque asigna el sistema para cada subproceso.
Para realizar una limpieza, presiona el botón Destroy All Threads.
3. Cómo ver los subprocesos en Perfetto
Perfetto es excelente para hacer un seguimiento de los ciclos de vida y los recuentos de subprocesos a lo largo del tiempo.

PerfettoSQL para el análisis de subprocesos
Puedes consultar el registro para contar exactamente cuántos subprocesos generó la aplicación:
SELECT p.name AS process_name, COUNT(t.id) as thread_count
FROM thread t JOIN process p USING (upid)
WHERE p.name = 'com.android.memorylab' AND t.name LIKE 'LeakedThread-%';
Se puede usar una consulta como esta para generar un segmento de depuración con un contador, como se muestra en la captura de pantalla.
← Código de la app | ↑ Arriba | Localidad →