Jak mogę odkryć wykorzystanie pamięci mojej aplikacji w systemie Android?

Jak mogę programowo znaleźć pamięć używaną w mojej aplikacji na Androida?

Mam nadzieję, że jest na to sposób. Plus, Jak uzyskać darmową pamięć telefonu też?

Author: Peter Mortensen, 2010-02-19

8 answers

Zauważ, ż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 rzeczywiście poprawnie interpretować cokolwiek liczb można dostać jest bardzo niska. (Prawie za każdym razem, gdy patrzę na liczby użycia pamięci z innymi inżynierami, zawsze trwa długa dyskusja na temat tego, co tak naprawdę oznaczają, co prowadzi tylko do niejasnego wniosku.)

Uwaga: mamy teraz znacznie bardziej rozbudowane dokumentacja zarządzanie pamięcią aplikacji , która obejmuje większość materiału tutaj i jest bardziej aktualna ze stanem Androida.

Pierwszą rzeczą jest prawdopodobnie przeczytanie ostatniej części tego artykułu, w którym omówiono sposób zarządzania pamięcią na Androidzie: {]}

Zmiany interfejsu API usługi począwszy od Androida 2.0

Teraz ActivityManager.getMemoryInfo() jest naszym najwyższym poziomem API do analizowania ogólnego zużycia pamięci. Jest to głównie po to, aby pomóc miernikowi aplikacji, jak zamknij system nie ma już pamięci dla procesów w tle, więc musi zacząć zabijać potrzebne procesy, takie jak usługi. W przypadku czystych aplikacji Java powinno to być mało użyteczne, ponieważ limit sterty Javy istnieje po części, aby jedna aplikacja nie była w stanie obciążać systemu do tego momentu.

Idąc na niższym poziomie, możesz użyć interfejsu API debugowania, aby uzyskać surowe informacje o zużyciu pamięci na poziomie jądra: android.os.Debugowanie.MemoryInfo

Uwaga zaczynająca się od 2.0 istnieje również API ActivityManager.getProcessMemoryInfo, aby uzyskać te 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 jaka jest różnica między Pss, PrivateDirty, i SharedDirty... teraz zaczyna się zabawa.

Wiele pamięci w Androidzie (i systemach Linux w ogóle) jest w rzeczywistości współdzielonych przez wiele procesów. Więc ile pamięci zużywa proces nie jasne. Dodaj do tego stronicowanie na dysk (nie mówiąc już o swapie, którego nie używamy na Androidzie), a jest jeszcze mniej jasne.

Tak więc, jeśli weźmiesz całą fizyczną PAMIĘĆ RAM zmapowaną do każdego procesu i zsumujesz wszystkie procesy, prawdopodobnie skończysz z liczbą znacznie większą niż rzeczywista całkowita pamięć RAM.

Liczba Pss jest metryką, którą oblicza jądro, biorąc pod uwagę współdzielenie pamięci - zasadniczo każda strona PAMIĘCI RAM w procesie jest skalowana przez stosunek liczby innych procesów również korzystających z tej strony. W ten sposób można (teoretycznie) dodać pss dla wszystkich procesów, aby zobaczyć całkowitą PAMIĘĆ RAM, której używają, i porównać pss między procesami, aby uzyskać przybliżony obraz ich względnej wagi.

Innym interesującym wskaźnikiem jest PrivateDirty, który jest w zasadzie ilością pamięci RAM wewnątrz procesu, której nie można pobrać na dysk (nie jest ona wspierana przez te same dane na dysku) i nie jest współdzielona z żadnymi innymi procesami. Another way to look jest to pamięć RAM, która stanie się dostępna dla systemu, gdy ten proces odejdzie (i prawdopodobnie szybko zostanie zamieniona na pamięć podręczną i inne jej zastosowania).

To w zasadzie API SDK do tego. Jest jednak więcej, co możesz zrobić jako programista ze swoim urządzeniem.

Używając adb, można uzyskać wiele informacji na temat wykorzystania pamięci przez uruchomiony system. Wspólne jest polecenie adb shell dumpsys meminfo, które wypluwa kilka informacji o wykorzystaniu pamięci każdego Proces Java, zawierający powyższe informacje, a także wiele innych rzeczy. Możesz również zaznaczyć nazwę lub pid pojedynczego procesu, aby zobaczyć, na przykład adb shell dumpsys meminfo system podaj mi proces systemowy:

** MEMINFO in pid 890 [system] **
                    native   dalvik    other    total
            size:    10940     7047      N/A    17987
       allocated:     8943     5516      N/A    14459
            free:      336     1531      N/A     1867
           (Pss):     4585     9282    11916    25783
  (shared dirty):     2184     3596      916     6696
    (priv dirty):     4504     5956     7456    17916

 Objects
           Views:      149        ViewRoots:        4
     AppContexts:       13       Activities:        0
          Assets:        4    AssetManagers:        4
   Local Binders:      141    Proxy Binders:      158
Death Recipients:       49
 OpenSSL Sockets:        0

 SQL
            heap:      205          dbFiles:        0
       numPagers:        0   inactivePageKB:        0
    activePageKB:        0

Górna sekcja jest główną, gdzie size jest całkowitym rozmiarem w przestrzeni adresowej określonej sterty, {[15] } jest kb rzeczywistych alokacji, które sterta myśli, że ma, {[16] } jest pozostałym kb wolnym od Sterty Dla dodatkowych alokacji, a pss i priv dirty są takie same jak omówione before specific to pages associated with each of the heaps.

Jeśli chcesz tylko przyjrzeć się zużyciu pamięci we wszystkich procesach, możesz użyć polecenia adb shell procrank. Wyjście tego na tym samym systemie wygląda następująco:

  PID      Vss      Rss      Pss      Uss  cmdline
  890   84456K   48668K   25850K   21284K  system_server
 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     336K     332K      99K      92K  /system/bin/installd
   60     396K     392K      93K      84K  /system/bin/keystore
   51     280K     276K      74K      68K  /system/bin/servicemanager
   54     256K     252K      69K      64K  /system/bin/debuggerd

Tutaj kolumny Vss i Rss są w zasadzie szumem (są to prosta przestrzeń adresowa i wykorzystanie pamięci RAM procesu, gdzie jeśli zsumujesz zużycie pamięci RAM między procesami, otrzymasz śmiesznie dużą liczbę).

Pss jest jak widzieliśmy wcześniej, i Uss jest Priv Dirty.

Ciekawostka: Pss i Uss są nieco (lub więcej niż nieco) inne niż to, co widzieliśmy w meminfo. Dlaczego? Cóż procrank używa innego mechanizmu jądra do zbierania swoich danych niż meminfo i dają nieco inne wyniki. Dlaczego? Szczerze mówiąc, nie mam pojęcia. Wierzę, że może być bardziej dokładny... ale tak naprawdę, to po prostu zostawić punkt: "weź wszelkie informacje o pamięci można uzyskać z ziarnkiem soli; często bardzo duże ziarno."

Wreszcie jest polecenie adb shell cat /proc/meminfo, które daje podsumowanie ogólnego wykorzystania pamięci systemu. Jest tu wiele danych, tylko kilka pierwszych liczb wartych omówienia (i pozostałe zrozumiane przez kilka osób, a moje pytania tych kilku osób na ich temat często skutkują sprzecznymi wyjaśnieniami): {]}

MemTotal:         395144 kB
MemFree:          184936 kB
Buffers:             880 kB
Cached:            84104 kB
SwapCached:            0 kB

MemTotal całkowita ilość pamięci dostępnej dla jądra i przestrzeni użytkownika (często mniejsza niż rzeczywista fizyczna PAMIĘĆ RAM urządzenia, ponieważ część tej pamięci RAM jest potrzebna do radia, buforów DMA, itp.).

MemFree to ilość pamięci RAM, która w ogóle nie jest używana. Liczba, którą tutaj widzisz, jest bardzo wysoka; zazwyczaj w systemie Android byłoby to tylko kilka MB, ponieważ staramy się używać dostępnej pamięci, aby utrzymać uruchomione procesy]}

Cached czy RAM jest używany do pamięci podręcznej systemu plików i innych tego typu rzeczy. Typowe systemy będą musiały mieć około 20MB, aby uniknąć wpadania w złe Stany przywoławcze; Android out of memory killer jest dostrojony do konkretnego systemu, aby upewnić się, że procesy w tle są zabijane, zanim pamięć podręczna zostanie zużyta zbyt dużo przez nich, aby doprowadzić do takiego stronicowania.

 974
Author: hackbod,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-08 18:02:51

Tak, możesz programowo uzyskać informacje o pamięci i zdecydować, czy chcesz wykonywać intensywną pracę z pamięcią.

Get VM Heap Size by calling:

Runtime.getRuntime().totalMemory();

Uzyskaj przydzieloną pamięć VM przez wywołanie:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Pobierz Limit rozmiaru sterty maszyny wirtualnej przez wywołanie:

Runtime.getRuntime().maxMemory()

Uzyskaj natywną Alokowaną pamięć przez wywołanie:

Debug.getNativeHeapAllocatedSize();

Zrobiłem aplikację, aby dowiedzieć się OutOfMemoryError zachowanie i pamięć monitora użycie.

Https://play.google.com/store/apps/details?id=net.coocood.oomresearch

Możesz uzyskać kod źródłowy na https://github.com/coocood/oom-research

 72
Author: coocood,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-01 05:49:41

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 do wyniku w Menedżerze activityManager.getProcessMemoryInfo()? Oczywiście chcesz, aby wynikowe dane miały znaczenie, więc dlaczego Google tak trudno jest skorelować wyniki? Obecny system nie działa nawet dobrze, jeśli chcę przetworzyć całe użycie pamięci, ponieważ zwracanym wynikiem jest tablica Androida.os.Debugowanie.Obiektów MemoryInfo, ale żaden z tych obiektów faktycznie powiedzieć, z jakimi PID są związane. Jeśli po prostu przekażesz tablicę wszystkich pid, nie będziesz miał możliwości zrozumienia wyników. Jak rozumiem, że jest to użycie, to bez znaczenia jest przekazywanie więcej niż jednego pid na raz, a jeśli tak, to po co robić to tak, że activityManager.getProcessMemoryInfo() pobiera tylko tablicę int?

 49
Author: Ryan Beesley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-13 18:32:33

Hackbod jest jedną z najlepszych odpowiedzi na Stack Overflow. Rzuca światło na bardzo niejasny temat. Bardzo mi to pomogło.

Innym naprawdę pomocnym zasobem jest ten film wideo: Google I / O 2011: Zarządzanie pamięcią dla aplikacji na Androida


UPDATE:

Process Stats, usługa pozwalająca dowiedzieć się, jak aplikacja zarządza pamięcią, wyjaśniona w poście na blogustatystyki procesów: zrozumienie, w jaki sposób Aplikacja korzysta z pamięci RAM autor: Dianne Hackborn:

 24
Author: Xavi Gil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-27 21:33:24

Android Studio 0.8.10 + wprowadził niezwykle przydatne narzędzie o nazwie Memory Monitor .

Tutaj wpisz opis obrazka

Do czego to jest dobre:

  • pokazanie dostępnej i używanej pamięci na wykresie oraz usuwanie śmieci wydarzenia w czasie.
  • szybkie sprawdzanie, czy powolność aplikacji może być związane z nadmiernym usuwaniem śmieci.
  • szybkie testowanie czy awarie aplikacji mogą być związane z brakiem pamięci.

Tutaj wpisz opis obrazka

Rysunek 1. Wymuszanie zdarzenia Gc (Garbage Collection) Na monitorze pamięci Androida

Możesz mieć wiele dobrych informacji na temat zużycia pamięci RAM aplikacji w czasie rzeczywistym, używając go.

 19
Author: Machado,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-12 12:40:20

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);
 16
Author: yanchenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-27 21:27:36

Dowiedzieliśmy się, że wszystkie standardowe sposoby uzyskania całkowitej pamięci bieżącego procesu mają pewne problemy.

  • Runtime.getRuntime().totalMemory(): zwraca tylko pamięć JVM
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() i wszystko inne oparte na /proc/meminfo - zwraca informacje o pamięci o wszystkich połączonych procesach (np. android_util_Process.cpp )
  • Debug.getNativeHeapAllocatedSize() - używa mallinfo(), które zwracają informacje o przydziale pamięci wykonywanym przez malloc() i tylko powiązane funkcje (Zobacz android_os_Debug.cpp )
  • Debug.getMemoryInfo() - robi robotę, ale jest za wolno. To trwa około 200ms na Nexus 6 dla jednego połączenia. Narzut wydajności czyni tę funkcję bezużyteczną dla nas, ponieważ nazywamy ją regularnie i każde wywołanie jest dość zauważalne (zobacz android_os_Debug.cpp )
  • ActivityManager.getProcessMemoryInfo(int[]) - połączenia wewnętrzne Debug.getMemoryInfo() (Zobacz ActivityManagerService.java)

Wreszcie, my

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 vmrss metric. Więcej szczegółów na ten temat można znaleźć tutaj: one, dwa i trzy.


P. S. zauważyłem, że temat nadal nie ma rzeczywistego i prostego fragmentu kodu, jak oszacować użycie pamięci prywatnej procesu, jeśli wydajność nie jest krytycznym wymogiem: {]}

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;
 2
Author: Dmitry Shesterkin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-14 04:04:28

Jest wiele odpowiedzi powyżej, które na pewno ci pomogą, ale (po 2 dniach badań nad narzędziami pamięci adb) myślę, że mogę pomóc z moją } opinią też.

Jak mówi Hackbod: Tak więc, jeśli weźmiesz całą fizyczną PAMIĘĆ RAM zmapowaną do każdego procesu i zsumujesz wszystkie procesy, prawdopodobnie skończysz z liczbą znacznie większą niż rzeczywista całkowita pamięć RAM. więc nie ma możliwości, aby uzyskać dokładną ilość pamięć na proces.

ale można się do tego zbliżyć przez jakąś logikę..i powiem jak..

Istnieją pewne API jak android.os.Debug.MemoryInfo i ActivityManager.getMemoryInfo() wymienione powyżej, które już mogą być czytane i używane, ale będę mówić o inny sposób

Więc najpierw musisz być użytkownikiem roota, aby to zadziałało. Pobierz konsolę z uprawnieniami roota wykonując su w procesie i uzyskaj jej output and input stream. Następnie przejść id\n (enter) in ouputstream and write it do przetwarzania danych wyjściowych, Jeśli otrzyma strumień wejściowy zawierający uid=0, jesteś użytkownikiem root.

Oto logika, której użyjesz W powyższym procesie

Gdy pojawi się ouputstream procesu przekaże ci polecenie (procrank, dumpsys meminfo itp...)z \n zamiast id i pobrać jego inputstream i odczytać, zapisać strumień w bajtach [], char [] itd.. użyj surowych danych..i jesteś skończony!!!!!

Pozwolenie:

<uses-permission android:name="android.permission.FACTORY_TEST"/>

Sprawdź, czy są użytkownikami 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();
 }
}

Logcat : wynik

Surowe dane są pobierane z konsoli w pojedynczym łańcuchu, a nie z dowolnego API, które jest złożone do przechowywania, ponieważ musisz je ręcznie rozdzielić.

to tylko próba, proszę Zasugeruj mi, jeśli coś przeoczyłem

 0
Author: Mohit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-02 14:11:04