Jak uzyskać aktualne zużycie pamięci w systemie Android?

108

Użyłem / proc / meminfo i przeanalizowałem odpowiedź na polecenie, jednak wynik pokazuje, że:

MemTotal: 94348 kB MemFree: 5784 kB

znaczy. pokazuje, że jest tylko 5 MB wolnej pamięci. Czy to możliwe z Androidem Mobile? Na moim telefonie jest zainstalowana tylko 5-6 aplikacji i żadne inne zadanie nie jest uruchomione. ale nadal to polecenie pokazuje, że jest bardzo mało wolnej pamięci.

Czy ktoś może to wyjaśnić? czy jest jakiś inny sposób uzyskania wykorzystania pamięci w systemie Android?

Badal
źródło
2
Czy chcesz zobaczyć wolną pamięć na urządzenie czy na aplikację? Jeśli na aplikację, należy ją obliczyć na stercie a-la Debug.getNativeHeapFreeSize().
Igor Ganapolsky
1
Aby obliczyć wolną pamięć (w pamięci RAM) przy użyciu / proc / meminfo, musisz uzyskać agregację MemFree , Buffers , Cached i SwapCached . W tym celu istnieje API dostarczane przez Androida, które działa na API 16 i na oddziałach. Meminfo jest pomocne, jeśli celujesz w starsze interfejsy API.
AB

Odpowiedzi:

174

OSTRZEŻENIE: Ta odpowiedź mierzy użycie pamięci / dostępną dla URZĄDZENIA. To NIE jest to, co jest dostępne w Twojej aplikacji. Aby zmierzyć, co robi Twoja aplikacja i jest DOZWOLONE, użyj odpowiedzi programisty Androida .


Dokumentacja systemu Android - ActivityManager.MemoryInfo

  1. parse / proc / meminfo polecenie. Kod referencyjny można znaleźć tutaj: Uzyskaj użycie pamięci w systemie Android

  2. użyj poniższego kodu i uzyskaj aktualną pamięć RAM:

    MemoryInfo mi = new MemoryInfo();
    ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    activityManager.getMemoryInfo(mi);
    double availableMegs = mi.availMem / 0x100000L;
    
    //Percentage can be calculated for API 16+
    double percentAvail = mi.availMem / (double)mi.totalMem * 100.0;
    

Wyjaśnienie liczby 0x100000L

1024 bytes      == 1 Kibibyte 
1024 Kibibyte   == 1 Mebibyte

1024 * 1024     == 1048576
1048576         == 0x100000

Jest dość oczywiste, że liczba jest używana do konwersji z bajtów na mebibajty

PS: musimy obliczyć całkowitą pamięć tylko raz. więc wywołaj punkt 1 tylko raz w swoim kodzie, a następnie możesz wywołać kod punktu 2 wielokrotnie.

Badal
źródło
Chcę sprawdzić rozmiar pamięci.Co to jest MemoryInfo?
Piraba
PIraba, jego klasa Android API. Sprawdź to tutaj developer.android.com/reference/android/app/… .
Badal
@SanjayJoshi To dlatego, że zmienna availMem zawiera pamięć w bajtach. 1024 bajty to 1 kilobajt, a 1024 kilobajty to 1 megabajt. Czyli 1024 * 1024 równa się 1048576
Rolf ツ
2
Zamień na podwójne powyżej, w przeciwnym razie percentAvail będzie wynosić 0
blueether
1
@Rolf ツ przepraszam, ale 1024 bajty to jeden kibibajt, a 1024 kibibajt to jeden MibiByte. Kilo i Mega to przedrostki dziesiętne. 1000 bajtów = 1 kilobajt. To również jest błędnie wyjaśnione w odpowiedzi.
JacksOnF1re
88

Zależy to od definicji zapytania pamięci, które chcesz uzyskać.


Zwykle chcesz poznać stan pamięci sterty, ponieważ jeśli zużywa ona zbyt dużo pamięci, pojawia się OOM i powoduje awarię aplikacji.

W tym celu możesz sprawdzić kolejne wartości:

final Runtime runtime = Runtime.getRuntime();
final long usedMemInMB=(runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB=runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;

Im bardziej zmienna „usedMemInMB” zbliża się do wartości „maxHeapSizeInMB”, tym bliżej availHeapSizeInMBzera, tym bliżej jest OOM. (Z powodu fragmentacji pamięci możesz dostać OOM, ZANIM to osiągnie zero.)

To również pokazuje narzędzie DDMS do wykorzystania pamięci.


Alternatywnie, istnieje rzeczywiste użycie pamięci RAM, czyli ile zużywa cały system - zobacz zaakceptowaną odpowiedź, aby to obliczyć.


Aktualizacja: ponieważ Android O sprawia, że ​​twoja aplikacja korzysta również z natywnej pamięci RAM (przynajmniej do przechowywania map bitowych, co jest zwykle głównym powodem dużego zużycia pamięci), a nie tylko sterty, rzeczy się zmieniły i otrzymujesz mniej OOM (ponieważ heap nie zawiera już bitmap, sprawdź tutaj ), ale nadal powinieneś mieć oko na użycie pamięci, jeśli podejrzewasz, że masz wycieki pamięci. W systemie Android O, jeśli masz wycieki pamięci, które powinny spowodować OOM w starszych wersjach, wygląda na to, że po prostu ulegnie awarii bez możliwości złapania go. Oto jak sprawdzić użycie pamięci:

val nativeHeapSize = Debug.getNativeHeapSize()
val nativeHeapFreeSize = Debug.getNativeHeapFreeSize()
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize

Ale uważam, że najlepiej byłoby użyć profilera IDE, który pokazuje dane w czasie rzeczywistym za pomocą wykresu.

Dobra wiadomość na Androidzie O jest taka, że ​​znacznie trudniej jest uzyskać awarie z powodu OOM przechowywania zbyt wielu dużych bitmap, ale zła wiadomość jest taka, że ​​nie sądzę, aby można było złapać taki przypadek w czasie wykonywania.


EDYCJA: wydaje się Debug.getNativeHeapSize()zmieniać w czasie, ponieważ pokazuje całkowitą maksymalną ilość pamięci dla Twojej aplikacji. Dlatego te funkcje są używane tylko przez program profilujący, aby pokazać, ile używa Twoja aplikacja.

Jeśli chcesz uzyskać rzeczywistą całkowitą i dostępną natywną pamięć RAM, użyj tego:

val memoryInfo = ActivityManager.MemoryInfo()
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(memoryInfo)
val nativeHeapSize = memoryInfo.totalMem
val nativeHeapFreeSize = memoryInfo.availMem
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize
Log.d("AppLog", "total:${Formatter.formatFileSize(this, nativeHeapSize)} " +
        "free:${Formatter.formatFileSize(this, nativeHeapFreeSize)} " +
        "used:${Formatter.formatFileSize(this, usedMemInBytes)} ($usedMemInPercentage%)")
programista Androida
źródło
Łał. Tak proste, ale tak prawdziwe!
Biegał
jakie jest rzeczywiste użycie pamięci? więc usedMemInMB w tym przypadku nie jest rzeczywistym zużyciem pamięci przez aplikację? Kiedy używam tego kodu, pokazuje mi, że zużycie wynosi około 50 MB, ale kiedy przechodzę do ustawień telefonu i widzę tam zużycie pamięci, moja aplikacja pokazuje 100 MB. dlaczego to różnica?
batmaci
1
@batmaci Pamięć sterty to tylko część całkowitego wykorzystania pamięci przez aplikację. Istnieje również natywne użycie pamięci, która jest zwykle używana do stron internetowych, gier i niektórych ciężkich celów. Zwykle aplikacje muszą szukać tylko w pamięci sterty, ponieważ jest ona dość niska w porównaniu z pamięcią RAM urządzenia, a jeśli do niej dotrą, aplikacja ulegnie awarii (nawet jeśli jest dużo wolnej pamięci RAM).
programista Androida
To doskonały fragment kodu, bardzo przydatny do sprawdzania OOM, wielkie dzięki.
aolphn
@AlphaOF Dziękuję, ale wszystko się zmieniło na Androidzie O. Zaktualizowałem odpowiedź, aby pasowała do istniejącej sytuacji.
programista Androida
29

Oto sposób obliczenia wykorzystania pamięci aktualnie uruchomionej aplikacji :

public static long getUsedMemorySize() {

    long freeSize = 0L;
    long totalSize = 0L;
    long usedSize = -1L;
    try {
        Runtime info = Runtime.getRuntime();
        freeSize = info.freeMemory();
        totalSize = info.totalMemory();
        usedSize = totalSize - freeSize;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return usedSize;

}
Sharmilee
źródło
4
Jest to proste podejście, ale jak wskazano w dokumentacji, metoda freeMemory () klasy Runtime zwraca ilość dostępnej pamięci dla bieżącego programu lub aplikacji. Dlatego pamiętaj o tym podczas używania.
Aksel Fatih
2
@Peter - Tak, jest "zła", ponieważ odpowiada na inne pytanie niż zostało zadane. Z drugiej strony jest to „właściwe” w odniesieniu do tego, co twórca aplikacji zwykle musi wiedzieć: rzadko ma znaczenie, jaki jest ogólny stan pamięci na URZĄDZENIU - jeśli użytkownik uruchomił wiele aplikacji, system operacyjny powinien wykorzystywać większość jego pamięć - w przeciwnym razie jest nieefektywna. System operacyjny musi wiedzieć, co daje zaakceptowana odpowiedź, aby wiedzieć, kiedy zacząć zabijać aplikacje, które nie były ostatnio używane. Ale programista aplikacji musi wiedzieć, co mówi TA odpowiedź (i podobna odpowiedź programisty Androida) w porównaniu z runtime.maxMemory.
ToolmakerSteve
To rozwiązanie działa tylko w pamięci udostępnionej aplikacji przez środowisko wykonawcze. Nie daje to wglądu w całą pamięć systemową, jak wymaga tego OP.
AB
Na wielu urządzeniach (np. Xiaomi) Runtime.freeMemory()zwraca 0 i Runtime.totalMemory()zwraca tylko aktualnie przydzieloną pamięć.
artem
17

Inny sposób (obecnie pokazuję 25 MB wolnego miejsca na moim G1):

MemoryInfo mi = new MemoryInfo();
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
long availableMegs = mi.availMem / 1048576L;
yanchenko
źródło
Hej Alex, wielkie dzięki za pomoc! Jeszcze 1 pytanie. Ten kod daje mi dostępną pamięć RAM. Chcę też wyświetlić całkowitą pamięć RAM. Jak to zdobyć?
Badal
@Badal Nie znam do tego API Java. Trzymaj się parsowania / proc / meminfo.
yanchenko
12

Filozofia zarządzania pamięcią w Linuksie to „Wolna pamięć to zmarnowana pamięć”.

Zakładam, że następne dwa wiersze pokażą, ile pamięci jest w "Buforach", a ile w "Buforach". Chociaż istnieje różnica między tymi dwoma (proszę nie pytać, jaka to różnica :), oba z grubsza sumują się do ilości pamięci używanej do buforowania danych plików i metadanych.

O wiele bardziej użytecznym przewodnikiem po zwolnieniu pamięci w systemie Linux jest free(1)polecenie; na moim komputerze zgłasza takie informacje:

darmowe -m
             łącznie używane bezpłatne udostępnione bufory buforowane
Mem: 5980 1055 4924 0 91 374
- / + bufory / pamięć podręczna: 589 5391
Zamiana: 6347 0 6347

+/- buffers / cache: line to magiczna linia, która informuje, że naprawdę mam około 589 megabajtów aktywnie potrzebnej pamięci procesowej i około 5391 megabajtów `` wolnej '' pamięci, w tym sensie, że 91 + 374 megabajtów buforów / pamięci podręcznej można wyrzucić, gdyby można było z niej skorzystać w innym miejscu.

(Moja maszyna działała przez około trzy godziny, prawie nic poza przepełnieniem stosu, dlatego mam tak dużo wolnej pamięci).

Jeśli system Android nie jest dostarczany z programem free(1), możesz samodzielnie obliczyć /proc/meminfoplik; Po prostu podoba mi się free(1)format wyjściowy. :)

sarnold
źródło
1
@Igor, to cat /proc/meminfozamiast tego będziesz chciał . Jest o wiele bardziej szczegółowy, ale MemFree. Buffers, i Cachedsą prawdopodobnie najważniejszymi wersami.
sarnold
6

Odsyłam do kilku prac.

odniesienie:

Ta metoda getMemorySize () jest zwracana MemorySize, która ma całkowity i wolny rozmiar pamięci.
Nie wierzę doskonale w ten kod.
Ten kod testuje na LG G3 cat.6 (v5.0.1)

    private MemorySize getMemorySize() {
        final Pattern PATTERN = Pattern.compile("([a-zA-Z]+):\\s*(\\d+)");

        MemorySize result = new MemorySize();
        String line;
        try {
            RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
            while ((line = reader.readLine()) != null) {
                Matcher m = PATTERN.matcher(line);
                if (m.find()) {
                    String name = m.group(1);
                    String size = m.group(2);

                    if (name.equalsIgnoreCase("MemTotal")) {
                        result.total = Long.parseLong(size);
                    } else if (name.equalsIgnoreCase("MemFree") || name.equalsIgnoreCase("Buffers") ||
                            name.equalsIgnoreCase("Cached") || name.equalsIgnoreCase("SwapFree")) {
                        result.free += Long.parseLong(size);
                    }
                }
            }
            reader.close();

            result.total *= 1024;
            result.free *= 1024;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    private static class MemorySize {
        public long total = 0;
        public long free = 0;
    }

Wiem, że Pattern.compile () jest kosztowny, więc możesz przenieść jego kod do elementu klasy.

Hogun
źródło
3

Spojrzałem na drzewo źródłowe Androida.

Wewnątrz com.android.server.am. ActivityManagerService.java (usługa wewnętrzna udostępniana przez android.app. ActivityManager ).

public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
    final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
    final long hiddenAppMem = mProcessList.getMemLevel(ProcessList.HIDDEN_APP_MIN_ADJ);
    outInfo.availMem = Process.getFreeMemory();
    outInfo.totalMem = Process.getTotalMemory();
    outInfo.threshold = homeAppMem;
    outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2));
    outInfo.hiddenAppThreshold = hiddenAppMem;
    outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
            ProcessList.SERVICE_ADJ);
    outInfo.visibleAppThreshold = mProcessList.getMemLevel(
            ProcessList.VISIBLE_APP_ADJ);
    outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
            ProcessList.FOREGROUND_APP_ADJ);
}

Wewnątrz android.os. Process.java

/** @hide */
public static final native long getFreeMemory();

/** @hide */
public static final native long getTotalMemory();

Wywołuje metodę JNI z android_util_Process.cpp

Wniosek

MemoryInfo.availMem = MemFree + Cached w / proc / meminfo.

Uwagi

Całkowita pamięć jest dodawana na poziomie API 16.

ragazenta
źródło
1

Możesz także użyć narzędzia DDMS, które jest częścią pakietu Android SDK. pomaga w uzyskaniu alokacji pamięci kodu java i natywnego kodu c / c ++, jak również.

vsmph
źródło
0
public static boolean isAppInLowMemory(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);

    return memoryInfo.lowMemory;
}
iMobaio
źródło
0
final long usedMemInMB=(runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB=runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;

To dziwny kod. Zwraca MaxMemory - (totalMemory - freeMemory). Jeśli freeMemory jest równe 0, to kod zwróci MaxMemory - totalMemory, więc może być większe lub równe 0. Dlaczego freeMemory nie jest używane?

Andrey
źródło
0

Oto inny sposób sprawdzenia wykorzystania pamięci w aplikacji:

adb shell dumpsys meminfo <com.package.name> -d

Przykładowe dane wyjściowe:

Applications Memory Usage (kB):
Uptime: 2896577 Realtime: 2896577

** MEMINFO in pid 2094 [com.package.name] **
                   Pss  Private  Private  Swapped     Heap     Heap     Heap
                 Total    Dirty    Clean    Dirty     Size    Alloc     Free
                ------   ------   ------   ------   ------   ------   ------
  Native Heap     3472     3444        0        0     5348     4605      102
  Dalvik Heap     2349     2188        0        0     4640     4486      154
 Dalvik Other     1560     1392        0        0
        Stack      772      772        0        0
    Other dev        4        0        4        0
     .so mmap     2749     1040     1220        0
    .jar mmap        1        0        0        0
    .apk mmap      218        0       32        0
    .ttf mmap       38        0        4        0
    .dex mmap     3161       80     2564        0
   Other mmap        9        4        0        0
      Unknown       76       76        0        0
        TOTAL    14409     8996     3824        0     9988     9091      256

 Objects
               Views:       30         ViewRootImpl:        2
         AppContexts:        4           Activities:        2
              Assets:        2        AssetManagers:        2
       Local Binders:       17        Proxy Binders:       21
    Death Recipients:        7
     OpenSSL Sockets:        0

 SQL
         MEMORY_USED:        0
  PAGECACHE_OVERFLOW:        0          MALLOC_SIZE:        0

Ogólne wykorzystanie pamięci:

adb shell dumpsys meminfo

https://developer.android.com/studio/command-line/dumpsys#meminfo

user1506104
źródło