Quellcode basiert auf: Android R
0. Vorwort
Tatsächlich wurde dumpsys meminfo vor langer Zeit im Artikel zum Anzeigen der Speichernutzung in Android analysiert , aber als ich kürzlich die Speicherdaten gezählt habe, habe ich festgestellt, dass sie nicht korrekt sind, also habe ich den Quellcode erneut analysiert Beim Anzeigen der Speichernutzung in Android In diesem Blogbeitrag wird der Prozess von meminfo service and dump unter AMS in Kombination mit dem Code detailliert analysiert.
1. Der Ausgangspunkt von meminfo dumpsys
Die Meminfo-Statistiken unter AMS werden über den dumpsys-Befehl ausgegeben, der in der bin-Datei unter /system/bin/ gespeichert ist. Das Quellcode-Verzeichnis befindet sich unter frameworks/native/cmds/dumpsys/ Details entnehmen Sie bitte dem dumpsys- Artikel in Android .
2. meminfo-Dienste – MemBinder
Die Dienste, die ausgegeben werden können, werden alle im ServiceManager hinzugefügt, beispielsweise wird meminfo in der Funktion setSystemProcess von frameworks/base/services/java/com/android/server/am/ActivityManagerService.java hinzugefügt:
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);
...
}
Notiz:
Durch step7 des dumpsys-Artikels in android wissen wir, dass das Dump-Flag angegeben werden muss, wenn ein Dienst (z. B. meminfo) eine Dump-Operation über den dumpsys-Befehl ausführen muss. Hier gibt meminfo das Dump-Flag als DUMP_FLAG_PRIORITY_HIGH an , wenn addService .
Schauen Sie unten:
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);
}
}
}
Darin sind zwei Member-Variablen definiert:
- mActivityManagerService: AMS-Objekte aufzeichnen;
- mPriorityDumper: Verbinden Sie sich über seine Schnittstelle dump() mit AMS.dumpApplicationMemoryUsage();
Die eigentliche Implementierung befindet sich in dumpApplicationMemoryUsage() von AMS.
3. dumpsys meminfo -h
Nachdem Sie dumpApplicationMemoryUsage() eingegeben haben, sehen Sie eine While-Schleife, um die an dumpsys meminfo angehängten Parameter zu analysieren. Sehen Sie sich hier zuerst -h an und verwenden Sie diesen Parameter, um andere Parameteroptionen von dumpsys meminfo besser zu verstehen.
Der Code wird hier nicht gepostet, schauen Sie sich einfach die Ausgabe des Terminals an:
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: Enthält alle verfügbaren Informationen, genauer gesagt enthält es -d, -s, -S;
- -d: muss Dalvik-Details enthalten;
- -c: Ausgabe in eine komprimierte, maschinenlesbare Darstellung;
- -s: Nur die Zusammenfassung des Anwendungsspeichers ausgeben;
- -S: Speichere die Swap-PSS-Informationen;
- --oom: nur die OOM adj-Informationen von RSS/PSS ausgeben;
- --local: Informationen nur lokal sammeln und nur wirksam werden, wenn dies durch -a oder -s angegeben wird;
- --package: Alle Prozesse ausgeben, die das angegebene Paket laden;
- --checkin: Daten zum Einchecken ausgeben;
- --proto: proto-Daten ausgeben;
- Auf dumpsys meminfo kann [Prozess] folgen, dieser Prozess kann der Prozessname oder die Prozess-PID sein;
4. CollectProcesses()
Nachdem die Befehlszeilenparameter von dumpsys meminfo analysiert wurden, wird collectProcesses() aufgerufen, um die Pakete oder PIDs zu bestätigen, die mit dem Befehl übereinstimmen:
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;
}
Der Code ist noch relativ klar:
- Wenn die Befehlszeile pid angibt, dann sammeln Sie diese Prozesse;
- Wenn packageName festgelegt ist, sammeln Sie diese Pakete;
- Wenn nicht gesetzt, alle LRU-Prozesse erfassen;
Beachten Sie, dass die in den ersten beiden Punkten gesammelte Prozedur möglicherweise leer ist, da die eingestellten Parameter möglicherweise falsch sind oder nicht zugeordnet werden können.
Zu diesem Zeitpunkt fordert das Terminal auch No process auf:
shift:/ # dumpsys meminfo 12345
Kein Prozess gefunden für: 12345
5. dumpApplicationMemoryUsage()
Die Menge an Code für diese Funktion ist zu groß, also analysieren wir hier die wichtigsten Punkte.
5.1 Mehrere Variablen 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();
}
Ganz am Anfang müssen Sie einige wichtige Initialisierungen beachten:
- Thread: Geben Sie IApplicationThread an.Wenn --local nicht gesetzt ist (die meisten davon werden nicht gesetzt), müssen die anwendungsseitigen Daten durch diesen Thread gelesen und durch Pipe an AMS übergeben werden .
- oomAdj: ein wichtiger Datensatz in dumpsys meminfo, für LMKD siehe den Artikel über die Algorithmusanalyse von adj score im Detail;
- pid: die pid der aktuellen Anwendung, sie wird zusammen mit dumpsys meminfo angezeigt;
- mi: der Kern der Speicherdaten, diese Variable wird unten im Detail erklärt, siehe Abschnitt 5.2 für Details ;
5.2 Debug.MemoryInfo()
Quellverzeichnis: frameworks/base/core/java/android/os/Debug.java
Viele native Schnittstellen werden hier bereitgestellt:
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 Debug.getMemoryInfo()
Dann schaut der Code in Abschnitt 5.1 weiter nach unten:
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 wird hauptsächlich verwendet, um detaillierte Informationen der Anwendung zu erhalten. Im Allgemeinen wird diese Variable auf „true“ gesetzt, wenn „pid“ oder „package“ oder direkt der Befehlsparameter „-s“ angegeben wird;
- Der Standardwert von brief ist false, der von MemBinder übergeben wird;
- opts.oomOnly wird nur dann auf true gesetzt, wenn --oom angegeben ist;
Wenn --oom nicht angegeben ist, durchläuft der Code den if-Fall und die detaillierten Speicherinformationen des Prozesses werden über Debug.getMemoryInfo() abgerufen:
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;
}
- Erhalten Sie die Speicherinformationen des Prozesses von /proc/ pid /smaps durch load_maps() ;
- Erhalten Sie Informationen zum Grafikspeicher über libmemtrack.so durch read_memtrack_memory();
- Setzen Sie die Gruppeninformationen von UNKNOWN, Dalvik und Heap auf Meminfo durch env->SetIntField() ;
- Andere Informationen über otherArray auf MemoryInfo.otherStats[] setzen;
5.3.1 load_maps()
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() ist eine libmeminfo.so-Schnittstelle, der Code befindet sich unter /system/memory/libmeminfo/.
Hier ist eine Zusammenfassung eines Call-Frame-Diagramms:
Er liest hauptsächlich den Prozess-smaps-Knoten. Entsprechend verschiedener Attribute wird der virtuelle Adressraum des Prozesses in mehrere VMAs aufgeteilt. Jeder VMA bildet eine doppelt verkettete Liste durch vm_next und vm_prev. Die verkettete Liste befindet sich in task_struct->mm_struct -> Mmap des Prozesses. Beim Lesen der smaps-Datei eines Prozesses über die proc-Schnittstelle findet der Kernel zuerst den Kopf der vma-Verknüpfungsliste des Prozesses, durchläuft jede vma in der verknüpften Liste, zählt die Verwendung dieser vma bis walk_page_vma und zeigt sie schließlich an .
Hier sind die Statistiken für einen der VMAs:
- Rss: Residenter physischer Speicher, der physische Speicher, der tatsächlich vom Prozess im RAM gehalten wird. RSS enthält den von der gemeinsam genutzten Bibliothek belegten Speicher, daher kann dies irreführend sein;
- Pss: anteilig verwendeter physikalischer Speicher, der Unterschied zwischen Pss und Rss besteht darin, dass Pss die durchschnittliche Speichernutzung gemeinsam genutzter Bibliotheken zählt. Wenn ein so 30 kb Speicher belegt und dieser so von 3 Prozessen geteilt wird, dann beträgt der von so belegte Speicher in der Statistik eines bestimmten Prozesses Pss 30/3 kb;
- Private_Dirty: Private Speicherdaten, einfach nur Dirty, das Gegenteil von Private_Clean. Dirty umfasst page_dirty und pte_dirty, page_dirty ist die sogenannte Dirty Page (die Datei wird im Speicher gelesen und modifiziert, sie wird als Dirty Page markiert). Für pte_dirty, wenn die vma als anonym verwendet wird, wird do_anonymous_page aufgerufen, wenn das Lesen und Schreiben dieser vma einen Seitenfehler auslöst.Wenn vma_flags VM_WRITE enthält, wird es das pte_mkdirty(entry)-Flag übergeben;
- Private_Clean: private saubere Daten, im Gegensatz zu Private_Dirty;
- Shared_Dirty: Wie bei Private_Dirty wird der Speicher von mindestens einem anderen Prozess referenziert, und mindestens ein Prozess modifiziert ihn;
- Shared_Clean: Dasselbe wie Private_Clean, im Gegensatz zu Shared_Dirty;
- Swap: Die auszutauschende native Seite kann SwapRss heißen;
- SwapPss: Native Seiten, die getauscht werden, gezählt nach Verhältnis;
Im Allgemeinen ist es in Android ZRAM, das die Menge an verfügbarem Speicher im System erhöht, indem Speicherseiten komprimiert und in einen dynamisch zugewiesenen Speicherauslagerungsbereich gestellt werden. Die komprimierten Seiten sind alle anonyme Seiten.
Aber der folgende Abschnitt von vma ist ein Datei-Mapping, aber es gibt auch ein Swap-Feld, weil diese Datei durch mmap in den Adressraum des Prozesses übertragen wird. Wenn das Tag MAP_PRIVATE enthält, bedeutet dies, dass es sich um eine Copy-on-Write-Zuordnung handelt.Obwohl es dateigestützt ist, werden die Daten beim Schreiben von Daten in diese Daten auf die anonyme Seite kopiert, siehe also oben Anonymous : Es ist auch 0.
5.3.2 Vergleich zwischen vma- und HEAP-Enumeration in smaps
[Haufen] [anon:libc_malloc] [anon:schild: [Anon: GWP-ASan |
HEAP_NATIVE |
[anon:dalvik-* |
HEAP_DALVIK_OTHER |
*.So | HEAP_SO |
*.Krug | HEAP_JAR |
*.apk | HEAP_APK |
*.ttf | HEAP_TTF |
*.odex (*.dex) |
HEAP_DEX ----HEAP_DEX_APP_DEX |
*.vdex (@boot /boot /apex) |
HEAP_DEX ----HEAP_DEX_BOOT_VDEX ----HEAP_DEX_APP_VDEX |
*.Hafer | HEAP_OAT |
*.Kunst *.art] (@boot /boot /apex) |
HEAP_ART ----HEAP_ART_BOOT ----HEAP_ART_APP |
/dev/ | HEAP_UNKNOWN_DEV |
/dev/kgsl-3d0 | HEAP_GL_DEV |
/dev/ashmem/CursorWindow | HEAP_CURSOR |
/dev/ashmem/jit-zygote-cache /memfd:jit-cache /memfd:jit-zygote-cache |
HEAP_DALVIK_OTHER |
/dev/ashm | HEAP_ASHMEM |
Siehe die Funktion load_maps() für detaillierte Informationen zu which_heap und sub_heap.
5.3.3 read_memtrack_memory()
Rufen Sie über diese Funktion den zugewiesenen Speicher des Prozesses auf der GPU ab.
Das Folgende ist das Anrufrahmendiagramm:
Das obige Bild liest den vom Prozess zugewiesenen Speicher auf der GPU aus, und jeder Hersteller kann unterschiedliche statistische Strategien haben, was auch der Grund für die HAL-Schicht ist.
5.4 thread.dumpMemInfo()
In Abschnitt 5.1 sind mehrere wichtige Variablen aufgelistet, von denen eine IApplicationThread ist, die für jeden Prozess einzigartig ist.
Dies ist natürlich der Fall, wenn opts.dumpDetails wahr ist, dh meminfo für eine einzelne Anwendung;
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();
}
Erstellen Sie ein TransferPipe-Objekt, das einen Thread und eine Pipe erstellt. Schreiben Sie dann Fd in den ActivithThread des App-Prozesses, und der ActivityThread analysiert die Speichernutzung des App-Prozesses. Wenn die Funktion go() aufgerufen wird, wird der von TransferPipe erstellte Thread gestartet und wartet auf die Pipe-Kommunikation.Wenn AMS die Daten über readFd erhält, benachrichtigt es den dumpsys- Prozess, um anzuzeigen, dass der Dump abgeschlossen ist.
Schauen wir uns dumpMemInfo() an:
---->
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);
}
}
Der detaillierte Code wird hier nicht weiter analysiert, aber ein Call-Frame-Diagramm zusammengefasst:
In dumpMeminfo() werden totalMemory und freeMemory des App-Prozesses dalvik über getRuntime() abgerufen, und dalvikAllocated wird berechnet, um die Speichernutzung der virtuellen Maschine des App-Prozesses zu erhalten.
Und verwenden Sie drei native Schnittstellen wie Debug.getNativeHeapSize(), um die native Nutzung des App-Prozesses zu zählen. Hier ist dasselbe wie in Abschnitt 5.3 oben, und es wird immer noch androi_os_Debug.cpp eingetragen:
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() gibt Statistiken zur Speicherzuweisung zurück. Die Funktion ist in android/bionic/libc/include/malloc.h deklariert und die Funktion ist in android/bionic/libc/bionic/malloc_common.cpp definiert:
extern "C" struct mallinfo mallinfo() {
auto dispatch_table = GetDispatchTable();
if (__predict_false(dispatch_table != nullptr)) {
return dispatch_table->mallinfo();
}
return Malloc(mallinfo)();
}
mallinfo() gibt hauptsächlich eine Struktur zurück, die die Speicherinformationen enthält, die von malloc() und den zugehörigen Funktionsaufrufen angefordert werden. Im Folgenden sind die spezifischen Informationen der Struktur mallinfo aufgeführt (siehe man für weitere Informationen):
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) */
};
Die drei Informationen, die wir hier zurückgeben:
- usmblks: der maximale Gesamtspeicher von alloc (Einheit: Byte);
- uordblks: Gesamtzuweisungsspeicher (Einheit: Byte);
- fordblks: Gesamter freier Speicher (Einheit: Byte);
Diese beiden Eingänge sind in der folgenden Abbildung dargestellt:
5.5 MemInfoReader
Bevor Sie MemInfoReader verwenden, müssen Sie zum Anfang des Codes zurückkehren und die Variable collectNative verstehen:
final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
Beim Ausgeben der Meminfo mehrerer Anwendungen oder aller Anwendungen wird die Variable collectNative auf true gesetzt; wenn nur die Meminfo einer einzelnen Anwendung gedruckt wird, ist der Wert falsch, sodass sie am Ende nicht gedruckt wird.
MemInfoReader memInfo = new MemInfoReader();
memInfo.readMemInfo();
Der detaillierte Code wird nicht mehr analysiert, hier eine Zusammenfassung eines aufrufenden Frames:
Die Ausgabe ist wie folgt:
6. meminfo-Datenanalyse
6.1 meminfo für eine einzelne Anwendung
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
Werfen wir einen Blick auf einige der wichtigsten Konzepte:
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
-
Dalvik Haufen
Von Dalvik-Zuweisungen in der Anwendung verwendeter RAM. Pss Total umfasst alle Zygote-Zuweisungen (gemessen an der Menge des gemeinsam genutzten Speichers zwischen Prozessen, wie in der PSS-Definition oben beschrieben). Die private Dirty-Nummer ist tatsächlicher RAM, der nur dem Heap Ihrer App zugewiesen ist und aus Ihren eigenen Zuweisungen und allen von Zygote zugewiesenen Seiten besteht, die geändert wurden, seit Zygote den App-Prozess geforkt hat.
-
Heap Alloc
ist die Speichermenge, die von Dalvik und dem nativen Heap-Allokator für Ihre Anwendung verfolgt wird. Dieser Wert ist größer als „Pss Total“ und „Private Dirty“, da Ihr Prozess von Zygote abzweigt und Zuweisungen enthält, die Ihr Prozess mit allen anderen Prozessen teilt.
-
.so mmap und .dex mmap
RAM belegt durch gemappten .so (nativ) und .dex (Dalvik oder ART) Code. Der Pss-Gesamtwert umfasst Plattformcode, der zwischen Apps geteilt wird; Private Clean ist der eigene Code Ihrer App. In der Regel ist der tatsächlich zugeordnete Speicher größer – der Arbeitsspeicher ist hier nur der Arbeitsspeicher, der derzeit von dem Code benötigt wird, den die App ausführt. .so mmaps haben jedoch einen größeren privaten Dirty RAM, da nativer Code modifiziert wird, wenn er in seine endgültige Adresse geladen wird.
-
.hafer mmap
Dies ist die Menge an RAM, die vom Code-Image verwendet wird, berechnet basierend auf vorgeladenen Klassen, die üblicherweise von mehreren Anwendungen verwendet werden. Dieses Bild wird von allen Anwendungen gemeinsam genutzt und wird nicht von einer bestimmten Anwendung beeinflusst.
-
.art mmap
Dies ist die vom Heap-Image belegte RAM-Größe, die auf der Grundlage vorab geladener Klassen berechnet wird, die üblicherweise von mehreren Anwendungen verwendet werden. Dieses Bild wird von allen Anwendungen gemeinsam genutzt und wird nicht von einer bestimmten Anwendung beeinflusst. Obwohl ein ART-Bild Objektinstanzen enthält, wird es dennoch nicht auf Ihre Heap-Größe angerechnet.
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).
Aus diesem Artikel gelernt:
Viele Prozesse in den allgemeinen Android- und Linux-Systemen teilen sich einen Teil des Arbeitsspeichers, sodass der von einem Prozess verwendete Arbeitsspeicher tatsächlich nicht sehr klar ist. Wenn Sie also den Arbeitsspeicher addieren, der tatsächlich von jedem Prozess belegt ist, werden Sie möglicherweise feststellen, dass das Ergebnis weitaus größer sein wird als Der tatsächliche Gesamtspeicher.
Pss ist der Wert, der vom Kernel basierend auf dem gemeinsam genutzten Speicher berechnet wird, und der Wert von Pss ist ein bestimmter Prozentsatz des gemeinsam genutzten Speichers. Auf diese Weise wird der gesamte RAM-Wert durch Addieren der Pss aller Prozesse erhalten, und das Nutzungsverhältnis dieser Prozesse kann auch durch den Interprozess-Pss-Wert erhalten werden.
PrivateDirty, bei dem es sich im Grunde um Speicher innerhalb eines Prozesses handelt, der nicht auf die Festplatte ausgelagert werden kann und nicht mit anderen Prozessen geteilt wird. Eine andere Möglichkeit, die Speichernutzung eines Prozesses zu betrachten, ist, wenn der Prozess endet, die Änderung des verfügbaren Speichers des Systems (er kann auch schnell in den Cache oder andere Prozesse, die den Speicherbereich verwenden, zusammengeführt werden).
其他类型 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 System-Meminfo-Statistiken
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 Gesamt-RAM
pw.print(stringifyKBSize(memInfo.getTotalSizeKb()));
Gesamt-RAM ist MemTotal in /proc/meminfo
6.2.2 Freier Arbeitsspeicher
pw.print(stringifyKBSize(cachedPss + memInfo.getCachedSizeKb()
+ memInfo.getFreeSizeKb()));
In Klammern:
- cached pss:Alle pss des Prozesses oom_score_adj >= 900
- zwischengespeicherter Kernel: /proc/meminfo.Buffers + /proc/meminfo.KReclaimable + /proc/meminfo.Cached - /proc/meminfo.Mapped
- kostenlos:/proc/meminfo.MemFree
6.2.3 ION
- Ionenheapgröße: 读取 /sys/kernel/ion/total_heaps_kb
- Ionenpoolgröße: Lesen Sie /sys/kernel/ion/total_pools_kb
- abgebildetes Ion + nicht abgebildetes Ion = Ionenhaufen
6.2.4 Benutzter Arbeitsspeicher
pw.print(stringifyKBSize(totalPss - cachedPss + kernelUsed));
in Klammern
- verwendete pss:totalPss - cachedPss
- KernelUsed: /proc/meminfo.Shmem + /proc/meminfo.SlabUnreclaim + VmallocUsed + /proc/meminfo.PageTables + /proc/meminfo.KernelStack + [ionHeap]
Achten Sie auf den obigen KernelStack, nur wenn der Kernel nicht mit CONFIG_VMAP_STACK konfiguriert ist , wird er hinzugefügt.
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 Verlorener RAM
final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();
Verlorener RAM = proc/meminfo.Memtotal – (totalPss – totalSwapPss) – /proc/meminfo.Memfree – /proc/meminfo.Cached – Kernel verwendet – zram verwendet
Notiz:
Daraus lässt sich grob ableiten
FREE RAM + USed RAM - totalSwapPss + Lost RAM + ZRAM = Total RAM
6.2.6 ZRAM
- physische Größe von zram: der dritte Parameter von /sys/block/zram0/mm_stat
- Swap total:proc/meminfo.SwapTotal
- Swap free:proc/meminfo.SwapFree
- Gebrauchter Tausch: Gesamttausch – kostenloser Tausch
6.2.7 Abstimmung
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();
large: Der Wert der Eigenschaft dalvik.vm.heapsize in MB
oom: der Wert des letzten Elements des mOomMinFree-Arrays in ProcessList
Wiederherstellungslimit: Der Wert der mCachedRestoreLevel-Variablen in ProcessList, der maximale Wert eines Prozesses vom Cache zum Hintergrund, im Allgemeinen 1/3 des letzten Werts von mOomMinFree.
Low-RAM: Ob es sich um ein Low-RAM-Gerät handelt, im Allgemeinen über prop ro.config.low_ram oder prop debug.force_low_ram, wenn ro.debuggable aktiviert ist.
high-end-gfx: ro.config.low_ram ist falsch, und ro.config.avoid_gfx_accel ist falsch, und der Wert von config config_avoidGfxAccel ist falsch.
Referenz: https://blog.csdn.net/feelabclihu/article/details/105534175