java - usage - android view memory




如何在Android中發現我的應用程序的內存使用情況? (6)

如何通過編程的方式找到我的Android應用程序中使用的內存?

我希望有辦法做到這一點。 另外,我如何獲得手機的免費記憶?


1)我猜不是,至少不是來自Java。
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

Android Studio 0.8.10+推出了一款名為Memory Monitor的令人難以置信的有用工具。

它有什麼好處:

  • 顯示圖形中的可用內存和已用內存以及隨著時間的推移垃圾收集事件。
  • 快速測試應用程序緩慢是否與過度的垃圾回收事件有關。
  • 快速測試應用程序崩潰是否與內存不足有關。

圖1.在Android內存監視器上強制GC(垃圾收集)事件

通過使用它,您可以獲得關於應用程序RAM實時消耗的大量良好信息。


上面有很多答案肯定會對你有所幫助,但是我(在經過2天對adb記憶工具的支持和研究後)認為我也可以幫助我。

正如Hackbod所說: 因此,如果您要將實際映射到每個進程的所有物理RAM並將所有進程累加起來,則最終可能會得到比實際總RAM更大的數字。 所以您無法獲得每個進程的精確內存量。

但是你可以通過一些邏輯來接近它......我會告訴你如何..

有一些API,如上面提到的android.os.Debug.MemoryInfoActivityManager.getMemoryInfo() ,您可能已經閱讀並使用了這些API,但我會討論另一種方法(間接方法)

所以首先你需要成為root用戶才能使用它。 通過在進程中執行su並獲取其output and input stream以root權限進入控制台。然後在output and input stream傳遞id\n (輸入)並將其寫入輸出流,如果將獲得包含uid=0 output and input stream ,則您是root用戶。

現在這裡是你將在上面的過程中使用的邏輯

當你得到輸入流的過程通過你命令(procrank,dumpsys meminfo等...)與\n而不是id並獲取它的輸入inputstream和讀取,存儲流,以字節,字符等..使用原始數據..你是做!!!!!

許可:

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

檢查你是否是root用戶:

try {
    // 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")) {                           
             // yes sir you are root user
         } 
    }
} catch (Exception e) {
}

su執行你的命令

try {
      // 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();
      }
} catch (Exception e) {
}

logcat:

這不是一個解決方案,因為您不會在某些實例或任何API中獲取數據,而是從一個字符串中獲取原始數據,就像從控制台獲取一樣,這很難管理,也很複雜,因此您需要手動分開它

這只是一個嘗試,如果我錯了,請糾正我


我們發現獲得當前進程總內存的所有標準方法都存在一些問題。

  • Runtime.getRuntime().totalMemory() :僅返回JVM內存
  • ActivityManager.getMemoryInfo()Process.getFreeMemory()和其他基於/proc/meminfo - 返回關於所有進程組合的內存信息(例如android_util_Process.cpp
  • Debug.getNativeHeapAllocatedSize() - 使用mallinfo() ,它返回由malloc()和相關函數執行的有關內存分配的信息(請參閱android_os_Debug.cpp
  • Debug.getMemoryInfo() - 完成這項工作,但速度太慢。 Nexus 6上的單次通話大約需要200ms 。 性能開銷使得這個函數對我們無用,因為我們經常調用它,每個調用都很明顯(請參閱android_os_Debug.cpp
  • ActivityManager.getProcessMemoryInfo(int[]) - 在內部調用Debug.getMemoryInfo() (請參閱ActivityManagerService.java

最後,我們結束了使用下面的代碼:

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;

它返回VmRSS度量。 你可以在這裡找到更多關於它的細節: onetwothree

PS我注意到,如果性能不是關鍵要求,那麼主題仍然缺乏一個實際簡單的代碼片斷,說明如何估計進程的私有內存使用情況:

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;

請注意,Linux等現代操作系統的內存使用情況非常複雜且難以理解。 事實上,你實際上正確解釋你得到的數字的機會是非常低的。 (幾乎每次我與其他工程師一起查看內存使用量數字時,總會長時間討論它們的實際含義,結果只會導致一個模糊的結論。)

注意:我們現在有更多關於管理您的應用程序內存的大量文檔,其中涵蓋了大部分內容,並且更新了Android的狀態。

首先,可能會閱讀本文的最後部分,其中討論瞭如何在Android上管理內存:

服務API更改從Android 2.0開始

現在ActivityManager.getMemoryInfo()是我們用於查看整體內存使用情況的最高級別API。 這主要是為了幫助應用程序衡量系統距離後台進程沒有更多內存有多近,因此需要開始查殺所需的進程(如服務)。 對於純Java應用程序,這應該沒什麼用處,因為Java堆限制的存在部分是為了避免一個應用程序能夠將系統強調到這一點。

在較低層次上,您可以使用調試API來獲取關於內存使用情況的原始內核級信息: android.os.Debug.MemoryInfo

注意,從2.0開始,還有一個API, ActivityManager.getProcessMemoryInfo ,用於獲取有關另一個進程的信息: ActivityManager.getProcessMemoryInfo(int[])

這將返回一個包含所有這些數據的低級別MemoryInfo結構:

    /** 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;

但是至於PssPrivateDirtySharedDirty之間的區別是什麼......現在好玩開始了。

Android(以及一般的Linux系統)中的大量內存實際上是在多個進程間共享的。 所以一個進程使用多少內存真的不清楚。 在該頁面的頂部添加磁盤(更不用說我們在Android上不使用的swap了),而且更加不清楚。

因此,如果您要將實際映射到每個進程的所有物理RAM,並將所有進程相加,那麼最終可能會得到比實際總RAM更大的數字。

Pss編號是內核計算時考慮到內存共享的度量標準 - 基本上,進程中的每個RAM頁面都按照使用該頁面的其他進程數量的比例進行縮放。 通過這種方式,您可以(理論上)在所有進程中添加pss以查看它們正在使用的總RAM,並比較進程之間的pss以粗略了解它們的相對權重。

另一個有趣的指標是PrivateDirty ,它基本上是進程內部的RAM數量,不能被分頁到磁盤(它不受磁盤上的相同數據支持),並且不與其他任何進程共享。 另一種看待這種情況的方式是當這個進程消失時(也可能很快包含到緩存和其他用途中),系統可以使用的RAM。

這幾乎是SDK的API。 但是,您可以使用設備作為開發人員進行更多操作。

使用adb ,您可以獲得關於正在運行的系統的內存使用情況的大量信息。 一個常見的命令是adb shell dumpsys meminfo命令,該命令將吐出關於每個Java進程的內存使用情況的大量信息,其中包含上述信息以及各種其他信息。 您還可以使用單個進程的名稱或pid來查看,例如adb shell dumpsys meminfo system給我係統進程:

** 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

頂部是主要部分,其中size是特定堆的地址空間中的總大小,allocation是堆認為它具有的實際分配的kb, free是剩餘的kb free,堆用於額外分配, psspriv dirty與之前討論的特定於與每個堆關聯的頁面相同。

如果您只想查看所有進程的內存使用情況,則可以使用命令adb shell procrank 。 這在同一個系統上的輸出如下所示:

  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

在這裡, VssRss列基本上是噪聲(這些是一個進程的直接地址空間和RAM使用情況,如果您在進程間累計RAM使用量,您會得到一個可笑的大數字)。

Pss就像我們以前見過的一樣,而UssPriv Dirty

有趣的事情在這裡註意: PssUss稍微(或稍多)不同於我們在meminfo看到的。 這是為什麼? procrank使用不同的內核機制來收集其數據,而不是meminfo ,它們給出的結果稍有不同。 這是為什麼? 老實說我沒有線索。 我相信procrank可能是更準確的......但實際上,這只是procrank :“記下你用鹽粒得到的任何記憶信息,通常是一顆非常大的粒。”

最後,命令adb shell cat /proc/meminfo給出系統總體內存使用情況的摘要。 這裡有很多數據,只有前幾個數字值得討論(而其餘的數據只有很少人能夠理解,而我對那些少數人的問題往往導致相互矛盾的解釋):

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

MemTotal是內核和用戶空間可用的內存總量(通常小於設備的實際物理RAM,因為無線電,DMA緩衝區等需要某些RAM)。

MemFree是根本沒有使用的RAM數量。 你在這裡看到的數字非常高, 通常在Android系統上,這只有幾MB,因為我們嘗試使用可用內存來保持進程運行

Cached是用於文件系統緩存和其他類似內容的RAM。 典型的系統需要20MB左右,以避免陷入惡劣的尋呼狀態; Android內存不足殺手會針對特定係統進行調整,以確保後台進程在高速緩存的RAM消耗太多而導致進行此類分頁之前被終止。


這是一項正在進行的工作,但這是我不明白的:

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");
    }
}

為什麼不將PID映射到activityManager.getProcessMemoryInfo()中的結果? 很明顯,你想讓結果數據有意義,所以為什麼谷歌很難將結果關聯起來呢? 如果我想處理整個內存使用情況,目前的系統甚至不能很好地工作,因為返回的結果是一個android.os.Debug.MemoryInfo對像數組,但是這些對像都沒有告訴你它們與哪些pid關聯。 如果你只是傳入所有pid的數組,你將無法理解結果。 據我了解這是使用,它使一次傳入多個pid沒有意義,然後如果是這樣,為什麼要使activityManager.getProcessMemoryInfo()只接受一個int數組?





memory-management