Jak mogę programowo znaleźć pamięć używaną w mojej aplikacji na Androida?
Mam nadzieję, że jest na to sposób. Dodatkowo, w jaki sposób mogę uzyskać bezpłatną pamięć telefonu?
Jak mogę programowo znaleźć pamięć używaną w mojej aplikacji na Androida?
Mam nadzieję, że jest na to sposób. Dodatkowo, w jaki sposób mogę uzyskać bezpłatną pamięć telefonu?
Odpowiedzi:
Pamiętaj, że użycie pamięci w nowoczesnych systemach operacyjnych, takich jak Linux, jest niezwykle skomplikowanym i trudnym do zrozumienia obszarem. W rzeczywistości szanse na to, że właściwie poprawnie interpretujesz otrzymane liczby, są bardzo niskie. (Prawie za każdym razem, gdy patrzę na numery wykorzystania pamięci z innymi inżynierami, zawsze trwa długa dyskusja na temat tego, co one właściwie oznaczają, co tylko prowadzi do niejasnych wniosków.)
Uwaga: mamy teraz znacznie obszerniejszą dokumentację dotyczącą zarządzania pamięcią aplikacji, która obejmuje większą część materiału tutaj i jest bardziej aktualna w stanie Androida.
Pierwszą rzeczą jest prawdopodobnie przeczytanie ostatniej części tego artykułu, w której omówiono sposób zarządzania pamięcią na Androidzie:
Zmiany interfejsu API usług począwszy od Androida 2.0
Teraz ActivityManager.getMemoryInfo()
jest nasz interfejs API najwyższego poziomu do sprawdzania ogólnego zużycia pamięci. Ma to na celu przede wszystkim pomóc aplikacji ocenić, jak blisko system nie ma już pamięci dla procesów w tle, a tym samym musi zacząć zabijać potrzebne procesy, takie jak usługi. W przypadku czystych aplikacji Java powinno to być mało przydatne, ponieważ limit sterty Java jest częściowo stosowany, aby jedna aplikacja nie była w stanie obciążać systemu do tego momentu.
Przechodząc na niższy poziom, możesz użyć interfejsu API debugowania, aby uzyskać nieprzetworzone informacje o zużyciu pamięci na poziomie jądra: android.os.Debug.MemoryInfo
Uwaga: począwszy od wersji 2.0 istnieje również interfejs API, ActivityManager.getProcessMemoryInfo
aby uzyskać informacje o innym procesie: ActivityManager.getProcessMemoryInfo (int [])
Zwraca niskopoziomową strukturę MemoryInfo ze wszystkimi tymi danymi:
/** The proportional set size for dalvik. */
public int dalvikPss;
/** The private dirty pages used by dalvik. */
public int dalvikPrivateDirty;
/** The shared dirty pages used by dalvik. */
public int dalvikSharedDirty;
/** The proportional set size for the native heap. */
public int nativePss;
/** The private dirty pages used by the native heap. */
public int nativePrivateDirty;
/** The shared dirty pages used by the native heap. */
public int nativeSharedDirty;
/** The proportional set size for everything else. */
public int otherPss;
/** The private dirty pages used by everything else. */
public int otherPrivateDirty;
/** The shared dirty pages used by everything else. */
public int otherSharedDirty;
Ale, co jest różnica między Pss
, PrivateDirty
i SharedDirty
... no teraz zaczyna się zabawa.
Dużo pamięci w systemie Android (i ogólnie w systemach Linux) jest faktycznie współdzielonych przez wiele procesów. Tak więc, ile pamięci zajmuje proces, nie jest do końca jasne. Dodanie tego stronicowania na dysk (nie mówiąc już o zamianie, której nie używamy na Androidzie), a jest jeszcze mniej jasne.
Zatem jeśli weźmiesz całą fizyczną pamięć RAM faktycznie zmapowaną do każdego procesu i zsumujesz wszystkie procesy, prawdopodobnie skończyłbyś z liczbą znacznie większą niż rzeczywista całkowita pamięć RAM.
Pss
Liczba jest metryka to Oblicza jądra, która bierze pod dzielenia pamięci konto - w zasadzie każdej strony pamięci RAM w procesie jest skalowany przez stosunek liczby innych procesów również przy użyciu tej strony. W ten sposób możesz (teoretycznie) dodać pss we wszystkich procesach, aby zobaczyć całkowitą pamięć RAM, której używają, i porównać pss między procesami, aby uzyskać przybliżone wyobrażenie o ich względnej wadze.
Inną interesującą miarą jest tutaj PrivateDirty
ilość pamięci RAM wewnątrz procesu, której nie można przywołać na dysk (nie jest ona wspierana tymi samymi danymi na dysku) i nie jest współdzielona z żadnymi innymi procesami. Innym sposobem, aby na to spojrzeć, jest pamięć RAM, która stanie się dostępna dla systemu, gdy proces ten zniknie (i prawdopodobnie szybko przejdzie do pamięci podręcznej i innych zastosowań).
To właściwie interfejsy API SDK do tego. Jest jednak więcej rzeczy, które możesz zrobić jako programista na swoim urządzeniu.
Używając adb
, możesz uzyskać wiele informacji na temat wykorzystania pamięci przez uruchomiony system. Często stosowanym jest polecenie, adb shell dumpsys meminfo
które wypluwa mnóstwo informacji na temat wykorzystania pamięci przez każdy proces Java, zawierające powyższe informacje, a także wiele innych rzeczy. Możesz także podać nazwę lub numer pid pojedynczego procesu, aby zobaczyć, na przykład adb shell dumpsys meminfo system
podaj mi proces systemowy:
** MEMINFO w pid 890 [system] ** natywny dalvik inne ogółem rozmiar: 10940 7047 N / A 17987 przydzielono: 8943 5516 nie dotyczy 14459 bezpłatnie: 336 1531 nie dotyczy 1867 (Pss): 4585 9282 11916 25783 (wspólne brudne): 2184 3596 916 6696 (priv dirty): 4504 5956 7456 17916 Obiekty Wyświetlenia: 149 Wyświetlenia: 4 AppContexts: 13 Działania: 0 Aktywa: 4 AssetManagers: 4 Lokalne segregatory: 141 Segregatory proxy: 158 Odbiorcy śmierci: 49 Gniazda OpenSSL: 0 SQL sterta: 205 db Pliki: 0 numPagers: 0 nieaktywne Strona KB: 0 activePageKB: 0
Górna sekcja jest główna, gdzie size
jest całkowity rozmiar w przestrzeni adresowej konkretnej sterty, allocated
jest to KB rzeczywistych przydziałów, które stos uważa, że ma, free
to pozostała wolna KB, którą sterty ma dla dodatkowych przydziałów, pss
i priv dirty
są takie same jak omówiono wcześniej dla stron powiązanych z każdym ze stosów.
Jeśli chcesz tylko sprawdzić wykorzystanie pamięci we wszystkich procesach, możesz użyć polecenia adb shell procrank
. Wynik tego w tym samym systemie wygląda następująco:
PID Vss Rss Pss Uss cmdline 890 84456K 48668K 25850K 21284K serwer_systemowy 1231 50748K 39088K 17587K 13792K com.android.launcher2 947 34488K 28528K 10834K 9308K com.android.wallpaper 987 26964K 26956K 8751K 7308K com.google.process.gapps 954 24300K 24296K 6249K 4824K com.android.phone 948 23020K 23016K 5864K 4748K com.android.inputmethod.latin 888 25728K 25724K 5774K 3668K zygote 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336 K 332 K 99 K 92 K / system / bin / installd 60 396K 392K 93K 84K / system / bin / magazyn kluczy 51 280 K 276 K 74 K 68 K / system / bin / serwisant 54 256 K 252 K 69 K 64 K / system / bin / debuggerd
W tym przypadku kolumny Vss
i Rss
są w zasadzie szumem (są to prosta przestrzeń adresowa i użycie pamięci RAM przez proces, gdzie jeśli zsumujesz użycie pamięci RAM w procesach, otrzymasz absurdalnie dużą liczbę).
Pss
jest jak widzieliśmy wcześniej i Uss
jest Priv Dirty
.
Interesująca rzecz do odnotowania tutaj: Pss
i Uss
są nieco (lub więcej niż nieco) inne niż to, co widzieliśmy meminfo
. Dlaczego? Cóż, procrank używa innego mechanizmu jądra do gromadzenia danych niż meminfo
robi i dają one nieco inne wyniki. Dlaczego? Szczerze mówiąc nie mam pojęcia. Wydaje mi się, że procrank
może być dokładniejszy ... ale tak naprawdę, po prostu zostaw punkt: „weź wszelkie informacje o pamięci, które otrzymujesz z dodatkiem ziarenka soli; często bardzo duże ziarno”.
Wreszcie istnieje polecenie, adb shell cat /proc/meminfo
które daje podsumowanie ogólnego wykorzystania pamięci przez system. Jest tu wiele danych, tylko kilka pierwszych liczb, które warto omówić (i pozostałe są zrozumiałe dla kilku osób, a moje pytania o te kilka osób na ich temat często powodują sprzeczne wyjaśnienia):
MemTotal: 395144 kB MemFree: 184936 kB Bufory: 880 kB W pamięci podręcznej: 84104 kB SwapCached: 0 kB
MemTotal
jest całkowitą ilością pamięci dostępnej dla jądra i przestrzeni użytkownika (często mniejszą niż faktyczna fizyczna pamięć RAM urządzenia, ponieważ część tej pamięci RAM jest potrzebna na radio, bufory DMA itp.).
MemFree
to ilość pamięci RAM, która w ogóle nie jest używana. Liczba, którą tu widzisz, jest bardzo wysoka; zwykle w systemie Android byłoby to zaledwie kilka MB, ponieważ staramy się wykorzystywać dostępną pamięć, aby procesy działały
Cached
jest używana pamięć RAM dla pamięci podręcznych systemu plików i innych podobnych rzeczy. Typowe systemy będą musiały mieć około 20 MB, aby uniknąć wpadania w zły stan stronicowania; zabójca pamięci poza systemem Android jest dostosowany do konkretnego systemu, aby upewnić się, że procesy w tle zostaną zabite, zanim pamięć RAM w pamięci podręcznej zostanie przez nich zbytnio zużyta, aby spowodować takie stronicowanie.
Tak, możesz uzyskać informacje o pamięci programowo i zdecydować, czy wykonać intensywną pracę.
Uzyskaj rozmiar stosu maszyn wirtualnych, dzwoniąc:
Runtime.getRuntime().totalMemory();
Uzyskaj przydzieloną pamięć VM, dzwoniąc:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
Uzyskaj limit rozmiaru stosu maszyn wirtualnych, dzwoniąc:
Runtime.getRuntime().maxMemory()
Uzyskaj natywną pamięć przydzieloną, dzwoniąc:
Debug.getNativeHeapAllocatedSize();
Zrobiłem aplikację, aby dowiedzieć się zachowanie OutOfMemoryError i monitorować użycie pamięci.
https://play.google.com/store/apps/details?id=net.coocood.oomresearch
Możesz pobrać kod źródłowy na https://github.com/coocood/oom-research
To jest praca w toku, ale tego nie rozumiem:
ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );
List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}
Collection<Integer> keys = pidMap.keySet();
for(int key : keys)
{
int pids[] = new int[1];
pids[0] = key;
android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
{
Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
}
}
Dlaczego PID nie jest mapowany na wynik w activityManager.getProcessMemoryInfo ()? Najwyraźniej chcesz, aby uzyskane dane były znaczące, więc dlaczego Google tak trudno skorelowało wyniki? Obecny system nawet nie działa dobrze, jeśli chcę przetworzyć całe użycie pamięci, ponieważ zwrócony wynik to tablica obiektów android.os.Debug.MemoryInfo, ale żaden z tych obiektów nie mówi ci, z którymi pidami są skojarzone. Jeśli po prostu przekażesz tablicę wszystkich ofert, nie będziesz w stanie zrozumieć wyników. Rozumiem, że jest użyteczny, dlatego nie ma sensu przekazywać więcej niż jednego pid na raz, a jeśli tak, to po co to robić, aby activityManager.getProcessMemoryInfo () pobierał tylko tablicę int?
Hackbod's jest jedną z najlepszych odpowiedzi na temat przepełnienia stosu. Rzuca światło na bardzo niejasny obiekt. Bardzo mi pomogło.
Innym naprawdę przydatnym źródłem jest ten film, który musisz zobaczyć: Google I / O 2011: Zarządzanie pamięcią dla aplikacji na Androida
AKTUALIZACJA:
Process Stats, usługa pozwalająca odkryć, w jaki sposób aplikacja zarządza pamięcią, wyjaśniona na blogu Statystyki procesu: Zrozumienie, w jaki sposób Twoja aplikacja korzysta z pamięci RAM przez Dianne Hackborn:
Android Studio 0.8.10+ wprowadziło niezwykle przydatne narzędzie o nazwie Memory Monitor .
Do czego służy:
- Wyświetlanie dostępnej i używanej pamięci na wykresie oraz zdarzeń usuwania śmieci w czasie.
- Szybkie testowanie, czy spowolnienie aplikacji może być związane z nadmiernymi zdarzeniami polegającymi na usuwaniu elementów bezużytecznych.
- Szybkie testowanie, czy awarie aplikacji mogą być związane z brakiem pamięci.
Rysunek 1. Wymuszanie zdarzenia GC (Garbage Collection) w Monitorze pamięci systemu Android
Korzystając z niej, możesz uzyskać wiele dobrych informacji na temat zużycia pamięci RAM w czasie rzeczywistym przez aplikację.
1) Chyba nie, przynajmniej nie z Javy.
2)
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);
Okazało się, że wszystkie standardowe sposoby uzyskania całkowitej pamięci bieżącego procesu mają pewne problemy.
Runtime.getRuntime().totalMemory()
: zwraca tylko pamięć JVMActivityManager.getMemoryInfo()
, Process.getFreeMemory()
I cokolwiek innego na podstawie /proc/meminfo
- zwraca informacje o pamięci wszystkich procesów połączonych (np android_util_Process.cpp )Debug.getNativeHeapAllocatedSize()
- używa, mallinfo()
które zwracają tylko informacje o przydziałach pamięci wykonywanych przez malloc()
i powiązanych funkcjach (patrz android_os_Debug.cpp )Debug.getMemoryInfo()
- wykonuje swoją pracę, ale jest zbyt wolna. Pojedyncza rozmowa trwa około 200 ms na Nexusie 6 . Narzut związany z wydajnością sprawia, że ta funkcja jest dla nas bezużyteczna, ponieważ nazywamy ją regularnie, a każde połączenie jest dość zauważalne (patrz android_os_Debug.cpp )ActivityManager.getProcessMemoryInfo(int[])
- wywołuje Debug.getMemoryInfo()
wewnętrznie (patrz ActivityManagerService.java )W końcu użyliśmy następującego kodu:
const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);
if( statsArr.Length < 2 )
throw new Exception("Parsing error of /proc/self/statm: " + stats);
return long.Parse(statsArr[1]) * pageSize;
Zwraca metrykę VmRSS . Możesz znaleźć więcej szczegółów na ten temat tutaj: jeden , dwa i trzy .
PS Zauważyłem, że temat nadal ma brak rzeczywistej i prosty fragment kodu, w jaki sposób oszacować prywatnego wykorzystania pamięci procesu, jeśli wydajność nie jest krytyczny wymóg:
Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
res += memInfo.getTotalPrivateClean();
return res * 1024L;
W Android Studio 3.0 wprowadzono Android-profiler, aby pomóc Ci zrozumieć, w jaki sposób aplikacja wykorzystuje zasoby procesora, pamięci, sieci i baterii.
https://developer.android.com/studio/profile/android-profiler
Istnieje wiele odpowiedzi powyżej, które na pewno ci pomogą, ale (po 2 dniach zakupu i badaniach nad narzędziami pamięci adb) myślę, że mogę również pomóc z moją opinią .
Jak mówi Hackbod: Zatem jeśli weźmiesz całą fizyczną pamięć RAM faktycznie zamapowaną do każdego procesu i zsumujesz wszystkie procesy, prawdopodobnie skończyłbyś z liczbą znacznie większą niż rzeczywista całkowita pamięć RAM. więc nie ma możliwości uzyskania dokładnej ilości pamięci na proces.
Ale możesz się do tego zbliżyć za pomocą logiki ... a ja powiem jak ...
Istnieje kilka interfejsów API podobnych
android.os.Debug.MemoryInfo
iActivityManager.getMemoryInfo()
wymienionych powyżej, o których być może czytałeś i których używasz, ale powiem o innym sposobie
Po pierwsze, musisz być użytkownikiem root, aby działał. Wejdź do konsoli z uprawnieniami roota, wykonując su
proces i zdobądź ją output and input stream
. Następnie przekaż id\n
(enter) w ouputstream i zapisz go do przetwarzania danych wyjściowych. Jeśli otrzymasz strumień wejściowy zawierający uid=0
, jesteś użytkownikiem root.
Oto logika, której użyjesz w powyższym procesie
Kiedy pojawi się ouputstream procesu , wydaj polecenie (procrank, dumpsys meminfo itp.) \n
Zamiast zamiast id i pobierz go inputstream
i przeczytaj, przechowuj strumień w bajtach [], char [] itd. Użyj surowych danych .. i są skończone!!!!!
pozwolenie :
<uses-permission android:name="android.permission.FACTORY_TEST"/>
Sprawdź, czy jesteś użytkownikiem root:
// su command to get root access
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream =
new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
// write id to console with enter
dataOutputStream.writeBytes("id\n");
dataOutputStream.flush();
String Uid = dataInputStream.readLine();
// read output and check if uid is there
if (Uid.contains("uid=0")) {
// you are root user
}
}
Wykonaj polecenie za pomocą su
Process process = Runtime.getRuntime().exec("su");
DataOutputStream dataOutputStream =
new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
// adb command
dataOutputStream.writeBytes("procrank\n");
dataOutputStream.flush();
BufferedInputStream bufferedInputStream =
new BufferedInputStream(process.getInputStream());
// this is important as it takes times to return to next line so wait
// else you with get empty bytes in buffered stream
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// read buffered stream into byte,char etc.
byte[] bff = new byte[bufferedInputStream.available()];
bufferedInputStream.read(bff);
bufferedInputStream.close();
}
}
Otrzymujesz nieprzetworzone dane w jednym ciągu z konsoli zamiast w jakimś przypadku z dowolnego interfejsu API, który jest skomplikowany do przechowywania, ponieważ będziesz musiał go oddzielić ręcznie .
To tylko próba, proszę zasugerować mi, jeśli coś przeoczyłem