Código fuente basado en: Android R
0. Prefacio
De hecho, dumpsys meminfo se analizó en el artículo sobre cómo ver el uso de la memoria en Android hace mucho tiempo , pero recientemente, cuando conté los datos de la memoria, descubrí que no era correcto, así que volví a analizar el código fuente. al ver el uso de la memoria en Android, Esta publicación de blog analizará el proceso del servicio meminfo y el volcado bajo AMS en detalle en combinación con el código.
1. El punto de partida de meminfo dumpsys
Las estadísticas de meminfo en AMS se vuelcan mediante el comando dumpsys, que se almacena en el archivo bin en /system/bin/. El directorio del código fuente se encuentra en frameworks/native/cmds/dumpsys/ Para obtener más información, consulte el artículo de dumpsys en android .
2. servicios meminfo -- MemBinder
Todos los servicios que se pueden volcar se agregan en ServiceManager. Por ejemplo, meminfo se agrega en la función setSystemProcess de frameworks/base/services/java/com/android/server/am/ActivityManagerService.java:
frameworks/base/services/core/java/com/android/server/am/AMS.java
public void setSystemProcess() {
try {
...
ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_HIGH);
...
}
Aviso:
A través del paso 7 del artículo dumpsys en android , sabemos que si un servicio (por ejemplo, meminfo) necesita realizar una operación de volcado a través del comando dumpsys, se debe especificar el indicador de volcado. Aquí meminfo especifica el indicador de volcado como DUMP_FLAG_PRIORITY_HIGH cuando addService .
Echa un vistazo a continuación:
frameworks/base/services/core/java/com/android/server/am/AMS.java
static class MemBinder extends Binder {
ActivityManagerService mActivityManagerService;
private final PriorityDump.PriorityDumper mPriorityDumper =
new PriorityDump.PriorityDumper() {
@Override
public void dumpHigh(FileDescriptor fd, PrintWriter pw, String[] args,
boolean asProto) {
dump(fd, pw, new String[] {"-a"}, asProto);
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args, boolean asProto) {
mActivityManagerService.dumpApplicationMemoryUsage(
fd, pw, " ", args, false, null, asProto);
}
};
MemBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
try {
mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(false);
if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
"meminfo", pw)) return;
PriorityDump.dump(mPriorityDumper, fd, pw, args);
} finally {
mActivityManagerService.mOomAdjuster.mCachedAppOptimizer.enableFreezer(true);
}
}
}
Hay dos variables miembro definidas en él:
- mActivityManagerService: registrar objetos AMS;
- mPriorityDumper: a través de su interfaz dump(), conéctese a AMS.dumpApplicationMemoryUsage();
La implementación real está en dumpApplicationMemoryUsage() de AMS.
3. dumpsys meminfo-h
Después de ingresar dumpApplicationMemoryUsage(), verá un ciclo while para analizar los parámetros adjuntos a dumpsys meminfo.Aquí, primero mire -h, y use este parámetro para comprender mejor otras opciones de parámetros de dumpsys meminfo.
El código no se publica aquí, solo mire la salida de la terminal:
meminfo dump options: [-a] [-d] [-c] [-s] [--oom] [process]
-a: include all available information for each process.
-d: include dalvik details.
-c: dump in a compact machine-parseable representation.
-s: dump only summary of application memory usage.
-S: dump also SwapPss.
--oom: only show processes organized by oom adj.
--local: only collect details locally, don't call process.
--package: interpret process arg as package, dumping all
processes that have loaded that package.
--checkin: dump data for a checkin
--proto: dump data to proto
If [process] is specified it can be the name or
pid of a specific process to dump.
- -a: Contiene toda la información disponible, más precisamente contiene -d, -s, -S;
- -d: necesita incluir detalles de dalvik;
- -c: volcar en una representación comprimida analizable por máquina;
- -s: solo volca el resumen de la memoria de la aplicación;
- -S: volcar la información de swap pss;
- --oom: solo descarga la información de ajuste OOM de RSS/PSS;
- --local: solo recopila información localmente y solo tiene efecto cuando se especifica mediante -a o -s;
- --paquete: volca todos los procesos que cargan el paquete especificado;
- --checkin: descarga los datos para registrarse;
- --proto: volcado de datos de prototipo;
- dumpsys meminfo puede ir seguido de [proceso], este proceso puede ser el nombre del proceso o el pid del proceso;
4. recogerProcesos()
Después de analizar los parámetros de la línea de comandos de dumpsys meminfo, se llamará a collectProcesses() para confirmar los paquetes o pids que coincidan con el comando:
frameworks/base/services/core/java/com/android/server/am/ProcessList.java
ArrayList<ProcessRecord> collectProcessesLocked(int start, boolean allPkgs, String[] args) {
ArrayList<ProcessRecord> procs;
if (args != null && args.length > start
&& args[start].charAt(0) != '-') {
procs = new ArrayList<ProcessRecord>();
int pid = -1;
try {
pid = Integer.parseInt(args[start]);
} catch (NumberFormatException e) {
}
for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
ProcessRecord proc = mLruProcesses.get(i);
if (proc.pid > 0 && proc.pid == pid) {
procs.add(proc);
} else if (allPkgs && proc.pkgList != null
&& proc.pkgList.containsKey(args[start])) {
procs.add(proc);
} else if (proc.processName.equals(args[start])) {
procs.add(proc);
}
}
if (procs.size() <= 0) {
return null;
}
} else {
procs = new ArrayList<ProcessRecord>(mLruProcesses);
}
return procs;
}
El código sigue siendo relativamente claro:
- Si la línea de comando especifica pid, recopile estos procesos;
- Si se establece packageName, recopile estos paquetes;
- Si no se establece, recopile todos los procesos de LRU;
Tenga en cuenta que el proceso recopilado en los dos primeros puntos puede estar vacío, porque los parámetros establecidos pueden ser falsos o no pueden coincidir.
En este momento, la terminal también indica Sin proceso:
shift:/ # dumpsys meminfo 12345
No se encontró ningún proceso para: 12345
5. dumpApplicationMemoryUsage()
La cantidad de código para esta función es demasiado grande, así que analicemos los puntos clave aquí.
5.1 Varias variables thread, pid, omadj, mi
frameworks/base/services/core/java/com/android/server/am/AMS.java
private final void dumpApplicationMemoryUsage() {
...
for (int i = numProcs - 1; i >= 0; i--) {
final ProcessRecord r = procs.get(i);
final IApplicationThread thread;
final int pid;
final int oomAdj;
final boolean hasActivities;
synchronized (this) {
thread = r.thread;
pid = r.pid;
oomAdj = r.getSetAdjWithServices();
hasActivities = r.hasActivities();
}
if (thread != null) {
if (mi == null) {
mi = new Debug.MemoryInfo();
}
Al principio, debe prestar atención a varias inicializaciones clave:
- subproceso: especifique IApplicationThread.Cuando --local no está configurado (la mayoría de ellos no se configurarán), los datos del lado de la aplicación deben leerse a través de este subproceso y pasarse a AMS a través de la canalización .
- oomAdj: un importante conjunto de datos en dumpsys meminfo, para LMKD, consulte el artículo sobre el análisis de algoritmos de la puntuación adj en detalle;
- pid: el pid de la aplicación actual, se mostrará junto con dumpsys meminfo;
- mi: el núcleo de los datos de la memoria, esta variable se explicará en detalle a continuación, vea la Sección 5.2 para más detalles ;
5.2 Depurar.MemoryInfo()
Directorio fuente: frameworks/base/core/java/android/os/Debug.java
Aquí se proporcionan muchas interfaces nativas:
public static native long getNativeHeapSize();
public static native long getNativeHeapAllocatedSize();
public static native long getNativeHeapFreeSize();
public static native void getMemoryInfo(MemoryInfo memoryInfo);
public static native boolean getMemoryInfo(int pid, MemoryInfo memoryInfo);
public static native long getPss();
public static native long getPss(int pid, long[] outUssSwapPssRss, long[] outMemtrack);
5.3 Depurar.getMemoryInfo()
Luego, el código en la Sección 5.1 continúa mirando hacia abajo:
if (opts.dumpDetails || (!brief && !opts.oomOnly)) {
reportType = ProcessStats.ADD_PSS_EXTERNAL_SLOW;
startTime = SystemClock.currentThreadTimeMillis();
if (!Debug.getMemoryInfo(pid, mi)) {
continue;
}
endTime = SystemClock.currentThreadTimeMillis();
hasSwapPss = mi.hasSwappedOutPss;
} else {
reportType = ProcessStats.ADD_PSS_EXTERNAL;
startTime = SystemClock.currentThreadTimeMillis();
long pss = Debug.getPss(pid, tmpLong, null);
if (pss == 0) {
continue;
}
mi.dalvikPss = (int) pss;
endTime = SystemClock.currentThreadTimeMillis();
mi.dalvikPrivateDirty = (int) tmpLong[0];
mi.dalvikRss = (int) tmpLong[2];
}
- opts.dumpDetails se utiliza principalmente para obtener información detallada de la aplicación. Generalmente, esta variable se establece en verdadero al especificar pid o paquete, o al especificar directamente el parámetro de comando -s;
- El valor predeterminado de breve es falso, que se pasa desde MemBinder;
- opts.oomOnly se establece en verdadero solo cuando se especifica --oom;
Cuando no se especifica --oom, el código pasará por el caso if y la información detallada de la memoria del proceso se obtendrá a través de Debug.getMemoryInfo():
frameworks/base/core/jni/android_os_Debug.cpp
static jboolean android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
jint pid, jobject object)
{
bool foundSwapPss;
stats_t stats[_NUM_HEAP];
memset(&stats, 0, sizeof(stats));
if (!load_maps(pid, stats, &foundSwapPss)) {
return JNI_FALSE;
}
struct graphics_memory_pss graphics_mem;
if (read_memtrack_memory(pid, &graphics_mem) == 0) {
stats[HEAP_GRAPHICS].pss = graphics_mem.graphics;
...
}
for (int i=_NUM_CORE_HEAP; i<_NUM_EXCLUSIVE_HEAP; i++) {
stats[HEAP_UNKNOWN].pss += stats[i].pss;
...
}
for (int i=0; i<_NUM_CORE_HEAP; i++) {
env->SetIntField(object, stat_fields[i].pss_field, stats[i].pss);
...
}
env->SetBooleanField(object, hasSwappedOutPss_field, foundSwapPss);
...
for (int i=_NUM_CORE_HEAP; i<_NUM_HEAP; i++) {
...
}
env->ReleasePrimitiveArrayCritical(otherIntArray, otherArray, 0);
return JNI_TRUE;
}
- Obtenga la información de memoria del proceso desde /proc/ pid /smaps a través de load_maps() ;
- Obtenga información de la memoria gráfica a través de libmemtrack.so mediante read_memtrack_memory();
- Configure la información de grupo de UNKNOWN, dalvik y heap en Meminfo a través de env->SetIntField() ;
- Establezca otra información en MemoryInfo.otherStats[] a través de otherArray;
5.3.1 cargar_mapas()
static bool load_maps(int pid, stats_t* stats, bool* foundSwapPss)
{
*foundSwapPss = false;
uint64_t prev_end = 0;
int prev_heap = HEAP_UNKNOWN;
std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
auto vma_scan = [&](const meminfo::Vma& vma) {
...
};
return meminfo::ForEachVmaFromFile(smaps_path, vma_scan);
}
meminfo::ForEachVmaFromFile() es la interfaz libmeminfo.so, el código se encuentra en /system/memory/libmeminfo/.
Aquí hay un resumen de un diagrama de marco de llamada:
Principalmente lee el nodo de smaps del proceso. Según diferentes atributos, el espacio de direcciones virtuales del proceso se dividirá en varios VMA. Cada VMA forma una lista doblemente enlazada a través de vm_next y vm_prev. La lista enlazada se encuentra en task_struct->mm_struct ->mapa del proceso. Al leer el archivo smaps de un proceso a través de la interfaz proc, el kernel primero encontrará el encabezado de la lista vinculada de vma del proceso, recorrerá cada vma en la lista vinculada, contará el uso de este vma a través de walk_page_vma y finalmente lo mostrará .
Aquí están las estadísticas de uno de los VMA:
- Rss: Memoria física residente, que es la memoria física que realmente tiene el proceso en la RAM. Rss incluye la memoria ocupada por la biblioteca compartida, por lo que puede resultar engañoso;
- Pss: memoria física utilizada en proporción, la diferencia entre Pss y Rss es que Pss cuenta el uso de memoria promedio de las bibliotecas compartidas. Si un so ocupa 30kb de memoria, y este so es compartido por 3 procesos, entonces la memoria ocupada por el so en las estadísticas de un determinado proceso Pss es 30/3 kb;
- Private_Dirty: datos de memoria privada, solo Dirty, lo contrario de Private_Clean. Dirty incluye page_dirty y pte_dirty, page_dirty es la llamada página sucia (el archivo se lee y modifica en la memoria, se marcará como página sucia). Para pte_dirty, cuando el vma se usa como anónimo, se llama a do_anonymous_page cuando leer y escribir este vma activa una falla de página.Si vma_flags contiene VM_WRITE, pasará el indicador pte_mkdirty(entrada);
- Private_Clean: datos limpios privados, opuestos a Private_Dirty;
- Shared_Dirty: igual que Private_Dirty, al menos otro proceso hace referencia a la memoria y al menos un proceso la está modificando;
- Shared_Clean: Igual que Private_Clean, opuesto a Shared_Dirty;
- Intercambio: la página nativa que se intercambiará se puede llamar SwapRss;
- SwapPss: Páginas nativas que se intercambian, contadas según el ratio;
En general, en Android, es ZRAM, que aumenta la cantidad de memoria disponible en el sistema al comprimir páginas de memoria y colocarlas en un área de intercambio de memoria asignada dinámicamente. Las páginas comprimidas son todas páginas anónimas.
Pero la siguiente sección de vma es una asignación de archivos, pero también hay un campo de intercambio, porque este archivo se transfiere al espacio de direcciones del proceso a través de mmap. Cuando hay MAP_PRIVATE en la etiqueta, significa que es un mapeo de copia en escritura. Aunque está respaldado por archivos, al escribir datos en estos datos, los datos se copiarán en la página anónima, así que consulte el Anónimo anterior . : También es 0.
5.3.2 Comparación entre enumeración vma y HEAP en smaps
[montón] [ano:libc_malloc] [ano:escudo: [anónimo:GWP-ASan |
HEAP_NATIVO |
[ano:dalvik-* |
HEAP_DALVIK_OTHER |
*.entonces | HEAP_SO |
*.frasco | HEAP_JAR |
*.apk | HEAP_APK |
*.ttf | HEAP_TTF |
*.odex (*.dex) |
HEAP_DEX ---- HEAP_DEX_APP_DEX |
*.vdex (@arranque /arranque /apex) |
HEAP_DEX ----HEAP_DEX_BOOT_VDEX ----HEAP_DEX_APP_VDEX |
*.avena | HEAP_OAT |
*.arte *.arte] (@arranque /arranque /apex) |
HEAP_ART ---- HEAP_ART_BOOT ---- HEAP_ART_APLICACIÓN |
/dev/ | HEAP_UNKNOWN_DEV |
/dev/kgsl-3d0 | HEAP_GL_DEV |
/dev/ashmem/CursorWindow | HEAP_CURSOR |
/dev/ashmem/jit-zygote-cache /memfd:jit-caché /memfd:jit-zygote-cache |
HEAP_DALVIK_OTHER |
/dev/ashmem | HEAP_ASHMEM |
Consulte la función load_maps() para obtener información detallada sobre which_heap y sub_heap.
5.3.3 read_memtrack_memory()
A través de esta función, obtenga la memoria asignada del proceso en la GPU.
El siguiente es el diagrama de marco de llamada:
La imagen de arriba lee la memoria asignada por el proceso en la GPU, y cada fabricante puede tener diferentes estrategias estadísticas, que también es la razón de la capa HAL.
5.4 hilo.dumpMemInfo()
Varias variables importantes se enumeran en la Sección 5.1, una de las cuales es IApplicationThread, que es única para cada proceso.
Por supuesto, este caso es cuando opts.dumpDetails es verdadero, es decir, meminfo para una sola aplicación;
try {
TransferPipe tp = new TransferPipe();
try {
thread.dumpMemInfo(tp.getWriteFd(),
mi, opts.isCheckinRequest, opts.dumpFullDetails,
opts.dumpDalvik, opts.dumpSummaryOnly, opts.dumpUnreachable, innerArgs);
tp.go(fd, opts.dumpUnreachable ? 30000 : 5000);
} finally {
tp.kill();
}
Cree un objeto TransferPipe, que creará un hilo y una tubería. Luego escriba Fd en ActivithThread del proceso de la aplicación y ActivityThread analizará el uso de memoria del proceso de la aplicación. Cuando se llama a la función go (), el hilo creado por TransferPipe se iniciará y esperará la comunicación de la tubería.Si AMS obtiene los datos a través de readFd, notificará al proceso dumpsys para indicar que el volcado se completó.
Veamos dumpMemInfo():
---->
frameworks/base/core/java/android/app/ActivityThread.java
public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
boolean dumpUnreachable, String[] args) {
FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
PrintWriter pw = new FastPrintWriter(fout);
try {
dumpMemInfo(pw, mem, checkin, dumpFullInfo, dumpDalvik, dumpSummaryOnly, dumpUnreachable);
} finally {
pw.flush();
IoUtils.closeQuietly(pfd);
}
}
El código detallado no se analizará más aquí, pero se resume un diagrama de marco de llamada:
En dumpMeminfo(), totalMemory y freeMemory del proceso de la aplicación dalvik se obtendrán a través de getRuntime(), y dalvikAllocated se calculará para obtener el uso de memoria de la máquina virtual del proceso de la aplicación.
Y use tres interfaces nativas como Debug.getNativeHeapSize() para contar el uso nativo del proceso de la aplicación. Aquí está lo mismo que en la Sección 5.3 anterior, y seguirá ingresando androi_os_Debug.cpp:
frameworks/base/core/jni/android_os_Debug.cpp
static jlong android_os_Debug_getNativeHeapSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.usmblks;
}
static jlong android_os_Debug_getNativeHeapAllocatedSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.uordblks;
}
static jlong android_os_Debug_getNativeHeapFreeSize(JNIEnv *env, jobject clazz)
{
struct mallinfo info = mallinfo();
return (jlong) info.fordblks;
}
mallinfo() devuelve estadísticas de asignación de memoria. La función se declara en android/bionic/libc/include/malloc.h, y la función se define en android/bionic/libc/bionic/malloc_common.cpp:
extern "C" struct mallinfo mallinfo() {
auto dispatch_table = GetDispatchTable();
if (__predict_false(dispatch_table != nullptr)) {
return dispatch_table->mallinfo();
}
return Malloc(mallinfo)();
}
mallinfo() principalmente devuelve una estructura, que contiene la información de memoria solicitada por malloc() y sus llamadas a funciones relacionadas. La siguiente es la información específica de la estructura mallinfo (ver man para más información):
struct mallinfo {
int arena; /* Non-mmapped space allocated (bytes) */
int ordblks; /* Number of free chunks */
int smblks; /* Number of free fastbin blocks */
int hblks; /* Number of mmapped regions */
int hblkhd; /* Space allocated in mmapped regions (bytes) */
int usmblks; /* Maximum total allocated space (bytes) */
int fsmblks; /* Space in freed fastbin blocks (bytes) */
int uordblks; /* Total allocated space (bytes) */
int fordblks; /* Total free space (bytes) */
int keepcost; /* Top-most, releasable space (bytes) */
};
Los tres datos que devolvemos aquí:
- usmblks: la memoria total máxima de alloc (unidad: byte);
- uordblks: memoria de asignación total (unidad: byte);
- fordblks: memoria libre total (unidad: byte);
Estas dos entradas se muestran en la siguiente figura:
5.5 MemInfoReader
Antes de usar MemInfoReader, debe volver al principio del código y comprender la variable collectNative:
final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
Al volcar el meminfo de varias aplicaciones o de todas las aplicaciones, la variable collectNative se establece en verdadero; si solo se imprime el meminfo de una sola aplicación, el valor es falso, por lo que no se imprimirá al final.
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
El código detallado ya no se analiza, aquí hay un resumen de un marco de llamada:
La salida es la siguiente:
6. análisis de datos meminfo
6.1 meminfo para una sola aplicación
shift:/ # dumpsys meminfo com.android.launcher3
Applications Memory Usage (in Kilobytes):
Uptime: 4443605 Realtime: 4443605
** MEMINFO in pid 1834 [com.android.launcher3] **
Pss Private Private Swap Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 13647 13548 0 0 16152 21672 16064 1962
Dalvik Heap 5338 5144 0 0 9496 6657 3329 3328
Dalvik Other 1317 1192 0 0 2376
Stack 636 636 0 0 644
Ashmem 2 0 0 0 20
Other dev 140 0 140 0 392
.so mmap 7350 232 12 0 57116
.jar mmap 2359 0 76 0 24984
.apk mmap 22108 0 15208 0 51748
.ttf mmap 110 0 0 0 376
.dex mmap 348 8 336 0 432
.oat mmap 1384 0 0 0 14560
.art mmap 2434 1608 4 0 20496
Other mmap 495 60 24 0 2896
Unknown 599 588 0 0 1128
TOTAL 58267 23016 15800 0 58267 28329 19393 5290
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 6756 29992
Native Heap: 13548 16152
Code: 15872 149484
Stack: 636 644
Graphics: 0 0
Private Other: 2004
System: 19451
Unknown: 6544
TOTAL PSS: 58267 TOTAL RSS: 202816 TOTAL SWAP (KB): 0
Objects
Views: 89 ViewRootImpl: 1
AppContexts: 10 Activities: 1
Assets: 14 AssetManagers: 0
Local Binders: 34 Proxy Binders: 43
Parcel memory: 13 Parcel count: 69
Death Recipients: 1 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 682
PAGECACHE_OVERFLOW: 292 MALLOC_SIZE: 117
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 16 20 1/14/1 /data/user/0/com.android.launcher3/databases/widgetpreviews.db
4 252 71 99/18/5 /data/user/0/com.android.launcher3/databases/app_icons.db
4 16 57 6/17/4 /data/user/0/com.android.launcher3/databases/launcher.db
Veamos algunos de los conceptos principales:
Java Heap: 24312 dalvik heap + .art mmap
Native Heap: 62256
Code: 66452 .so mmap + .jar mmap + .apk mmap + .ttf mmap + .dex mmap + .oat mmap
Stack: 84
Graphics: 5338 Gfx dev + EGL mtrack + GL mtrack
Private Other: 9604 TotalPrivateClean + TotalPrivateDirty - java - native - code - stack - graphics
System: 12900 TotalPss - TotalPrivateClean - TotalPrivateDirty
https://developer.android.com/studio/profile/investigate-ram?hl=zh-cn
-
Montón de Dalvík
RAM utilizada por las asignaciones de Dalvik en la aplicación. Pss Total incluye todas las asignaciones de Zygote (medidas por la cantidad de memoria compartida entre procesos como se describe en la definición de PSS anterior). El número Private Dirty es RAM real asignada solo al montón de su aplicación, que consiste en sus propias asignaciones y cualquier página asignada de Zygote que se haya modificado desde que Zygote bifurcó el proceso de la aplicación.
-
Asignación de montón
es la cantidad de memoria rastreada por Dalvik y el asignador de montón nativo para su aplicación. Este valor es mayor que Pss Total y Private Dirty porque su proceso se bifurca de Zygote y contiene asignaciones que su proceso comparte con todos los demás procesos.
-
.so mmap y .dex mmap
RAM ocupada por código mapeado .so (nativo) y .dex (Dalvik o ART). El valor de Pss Total incluye el código de plataforma compartido entre aplicaciones; Private Clean es el propio código de su aplicación. Por lo general, la memoria mapeada real es más grande: la RAM aquí es solo la RAM requerida actualmente por el código que está ejecutando la aplicación. Sin embargo, los .so mmaps tienen RAM sucia privada más grande porque el código nativo se modifica cuando se carga en su dirección final.
-
mapa de avena
Esta es la cantidad de RAM utilizada por la imagen del código, calculada en base a clases precargadas comúnmente utilizadas por varias aplicaciones. Esta imagen se comparte entre todas las aplicaciones y no se ve afectada por una aplicación específica.
-
mapa de arte
Esta es la cantidad de RAM ocupada por la imagen del montón, calculada en base a clases precargadas comúnmente utilizadas por varias aplicaciones. Esta imagen se comparte entre todas las aplicaciones y no se ve afectada por una aplicación específica. Aunque una imagen ART contiene instancias de objetos, aún no cuenta para el tamaño de almacenamiento dinámico.
But as to what the difference is between "Pss", "PrivateDirty", and "SharedDirty"... well now the fun begins.
A lot of memory in Android (and Linux systems in general) is actually shared across multiple processes.
So how much memory a processes uses is really not clear. Add on top of that paging out to disk (let alone swap which we don't use on
Android) and it is even less clear.
Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes,
you would probably end up with a number much greater than the actual total RAM.
The Pss number is a metric the kernel computes that takes into account memory sharing -- basically each page of RAM in a process is
scaled by a ratio of the number of other processes also using that page. This way you can (in theory) add up the pss across all
processes to see the total RAM they are using, and compare pss between processes to get a rough idea of their relative weight.
The other interesting metric here is PrivateDirty, which is basically the amount of RAM inside the process that can not be paged
to disk (it is not backed by the same data on disk), and is not shared with any other processes. Another way to look at this is the
RAM that will become available to the system when that process goes away (and probably quickly subsumed into caches and other uses
of it).
Aprendido de este artículo:
Muchos procesos en los sistemas generales de Android y Linux compartirán algo de memoria, por lo que la memoria utilizada por un proceso en realidad no está muy clara, por lo que si agrega la memoria realmente ocupada por cada proceso, puede encontrar que el resultado será mucho más que el mem total real.
Pss es el valor calculado por el kernel basado en la memoria compartida, y el valor de Pss es un cierto porcentaje de la memoria compartida. De esta forma, el valor de RAM total se obtiene sumando los Pss de todos los procesos, y la tasa de uso de estos procesos también se puede obtener a través del valor de Pss entre procesos.
PrivateDirty, que es básicamente memoria dentro de un proceso que no se puede paginar en el disco y no se comparte con otros procesos. Otra forma de ver el uso de la memoria de un proceso es cuando finaliza el proceso, el cambio de la memoria disponible del sistema (también se puede fusionar rápidamente en el caché u otros procesos que usan el área de memoria).
其他类型 smap 路径名称 描述
Ashmem /dev/ashmem 匿名共享内存用来提供共享内存通过分配一个多个进程
可以共享的带名称的内存块
Other dev /dev/ 内部driver占用的在 “Other dev”
.so mmap .so C 库代码占用的内存
.jar mmap .jar Java 文件代码占用的内存
.apk mmap .apk apk代码占用的内存
.ttf mmap .ttf ttf 文件代码占用的内存
.dex mmap .dex Dex 文件代码占用的内存
Other mmap 其他文件占用的内存
6.2 Estadísticas de meminfo del sistema
Total RAM: 3,768,168K (status normal)
Free RAM: 2,186,985K ( 64,861K cached pss + 735,736K cached kernel + 1,386,388K free)
ION: 58,408K ( 53,316K mapped + -128K unmapped + 5,220K pools)
Used RAM: 1,440,349K (1,116,469K used pss + 323,880K kernel)
Lost RAM: 140,822K
ZRAM: 12K physical used for 0K in swap (2,097,148K total swap)
Tuning: 256 (large 512), oom 640,000K, restore limit 213,333K (high-end-gfx)
6.2.1 RAM total
pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
La RAM total es MemTotal en /proc/meminfo
6.2.2 RAM libre
pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()));
Entre paréntesis:
- cached pss:Todos los pss del proceso oom_score_adj >= 900
- kernel en caché:/proc/meminfo.Buffers + /proc/meminfo.KReclaimable + /proc/meminfo.Cached - /proc/meminfo.Mapped
- gratis:/proc/meminfo.MemFree
6.2.3 ION
- tamaño del montón de iones: 读取 /sys/kernel/ion/total_heaps_kb
- tamaño del grupo de iones: leer /sys/kernel/ion/total_pools_kb
- ion mapeado + ion no mapeado = montón de iones
6.2.4 RAM utilizada
pw.print(stringifyKBSize(totalPss - cachedPss + kernelUsed));
entre paréntesis
- pss usado:totalPss - cachedPss
- KernelUsed:/proc/meminfo.Shmem + /proc/meminfo.SlabUnreclaim + VmallocUsed + /proc/meminfo.PageTables + /proc/meminfo.KernelStack + [ionHeap]
Preste atención al KernelStack anterior, solo cuando el kernel no esté configurado con CONFIG_VMAP_STACK , se agregará.
frameworks/base/core/java/com/android/internal/util/MemInfoReader.java
public long getKernelUsedSizeKb() {
long size = mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB_UNRECLAIMABLE]
+ mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES];
if (!Debug.isVmapStack()) {
size += mInfos[Debug.MEMINFO_KERNEL_STACK];
}
return size;
}
6.2.5 RAM perdida
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
RAM perdida = proc/meminfo.Memtotal - (totalPss - totalSwapPss) - /proc/meminfo.Memfree - /proc/meminfo.Cached - kernel usado - zram usado
Aviso:
Se puede inferir aproximadamente de aquí
RAM LIBRE + RAM UTILIZADA - totalSwapPss + RAM perdida + ZRAM = RAM total
6.2.6 ZRAM
- Tamaño físico de zram: el tercer parámetro de /sys/block/zram0/mm_stat
- total de intercambio:proc/meminfo.
- libre de intercambio:proc/meminfo.SwapFree
- canje usado: canje total - canje gratis
6.2.7 Afinación
pw.print(" Tuning: ");
pw.print(ActivityManager.staticGetMemoryClass());
pw.print(" (large ");
pw.print(ActivityManager.staticGetLargeMemoryClass());
pw.print("), oom ");
pw.print(stringifySize(
mProcessList.getMemLevel(ProcessList.CACHED_APP_MAX_ADJ), 1024));
pw.print(", restore limit ");
pw.print(stringifyKBSize(mProcessList.getCachedRestoreThresholdKb()));
if (ActivityManager.isLowRamDeviceStatic()) {
pw.print(" (low-ram)");
}
if (ActivityManager.isHighEndGfx()) {
pw.print(" (high-end-gfx)");
}
pw.println();
grande: el valor de la propiedad dalvik.vm.heapsize, en MB
oom: el valor del último elemento de la matriz mOomMinFree en ProcessList
límite de restauración: el valor de la variable mCachedRestoreLevel en ProcessList, el valor máximo de un proceso desde el almacenamiento en caché hasta el fondo, generalmente 1/3 del último valor de mOomMinFree.
low-ram: si se trata de un dispositivo con poca memoria RAM, generalmente a través de prop ro.config.low_ram o prop debug.force_low_ram cuando ro.debuggable está habilitado.
high-end-gfx: ro.config.low_ram es falso, y ro.config.avoid_gfx_accel es falso, y el valor de config_avoidGfxAccel es falso.
Referencia: https://blog.csdn.net/feelabclihu/article/details/105534175