如何以编程方式查找Android应用程序上使用的内存?

我希望有办法做到这一点。此外,我如何获得手机的空闲内存?


当前回答

注意,像Linux这样的现代操作系统上的内存使用是一个极其复杂且难以理解的领域。事实上,你真正正确解释你得到的任何数字的机会都极低。(几乎每次我和其他工程师一起查看内存使用率数据时,都会有一场漫长的讨论,讨论它们的实际含义,结果只能得出一个模糊的结论。)

注意:我们现在有更多关于管理应用程序内存的文档,其中涵盖了这里的大部分内容,并且更了解Android的最新状态。

首先可能要阅读本文的最后一部分,其中讨论了如何在Android上管理内存:

从Android 2.0开始的服务API更改

现在ActivityManager.getMemoryInfo()是我们查看总体内存使用情况的最高级别API。这主要是为了帮助应用程序判断系统接近没有更多内存用于后台进程的程度,从而需要开始杀死所需的进程(如服务)。对于纯Java应用程序,这应该没有什么用处,因为Java堆的限制部分是为了避免一个应用程序对系统造成压力。

在较低级别,您可以使用DebugAPI获取有关内存使用情况的原始内核级别信息:android.os.Debug.MemoryInfo

注意,从2.0开始,还有一个API ActivityManager.getProcessMemoryInfo,用于获取有关另一个进程的信息:ActivityManageer.getProcessMemooryInfo(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;

但关于PS、PrivateDirty和SharedDirty之间的区别。。。现在,乐趣开始了。

Android(以及一般的Linux系统)中的大量内存实际上是在多个进程中共享的。因此,一个进程使用多少内存还不清楚。再加上对磁盘的分页(更不用说我们在Android上不使用的交换),这就更不清楚了。

因此,如果您将实际映射到每个进程中的所有物理RAM相加,您可能会得到一个比实际总RAM大得多的数字。

Pss数是内核计算的一个度量,它考虑了内存共享——基本上,一个进程中的每一个RAM页面都是由使用该页面的其他进程数量的比率来缩放的。通过这种方式,您可以(理论上)将所有进程的pss相加,以查看它们正在使用的总RAM,并比较进程之间的pss,以大致了解它们的相对权重。

这里另一个有趣的度量是PrivateVirtualy,它基本上是进程内部无法分页到磁盘的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是特定堆的地址空间中的总大小,allocated是堆认为它拥有的实际分配的kb,free是堆剩余的kb空闲空间,用于额外分配,pss和priv-dirty与前面讨论的相同,具体到与每个堆相关联的页面。

如果您只想查看所有进程的内存使用情况,可以使用adbshellprocrank命令。同一系统上的输出如下:

  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

在这里,Vss和Rss列基本上是噪声(这是一个进程的直接地址空间和RAM使用情况,如果将进程之间的RAM使用情况相加,就会得到一个荒谬的大数字)。

PS是我们以前见过的,US是Priv Dirty。

这里值得注意的是:Pss和Us与我们在meminfo中看到的略有不同。为什么?Well procrank使用与meminfo不同的内核机制来收集数据,并且它们给出的结果略有不同。为什么?老实说,我一点线索也没有。我相信procrank可能更准确。。。但事实上,这只是离开了重点:“用一粒盐来获取任何记忆信息;通常是一粒很大的盐。”

最后是命令adbshellcat/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消耗过多而导致此类分页之前被杀死。

其他回答

1) 我想不会,至少不会来自Java。2)

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

注意,像Linux这样的现代操作系统上的内存使用是一个极其复杂且难以理解的领域。事实上,你真正正确解释你得到的任何数字的机会都极低。(几乎每次我和其他工程师一起查看内存使用率数据时,都会有一场漫长的讨论,讨论它们的实际含义,结果只能得出一个模糊的结论。)

注意:我们现在有更多关于管理应用程序内存的文档,其中涵盖了这里的大部分内容,并且更了解Android的最新状态。

首先可能要阅读本文的最后一部分,其中讨论了如何在Android上管理内存:

从Android 2.0开始的服务API更改

现在ActivityManager.getMemoryInfo()是我们查看总体内存使用情况的最高级别API。这主要是为了帮助应用程序判断系统接近没有更多内存用于后台进程的程度,从而需要开始杀死所需的进程(如服务)。对于纯Java应用程序,这应该没有什么用处,因为Java堆的限制部分是为了避免一个应用程序对系统造成压力。

在较低级别,您可以使用DebugAPI获取有关内存使用情况的原始内核级别信息:android.os.Debug.MemoryInfo

注意,从2.0开始,还有一个API ActivityManager.getProcessMemoryInfo,用于获取有关另一个进程的信息:ActivityManageer.getProcessMemooryInfo(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;

但关于PS、PrivateDirty和SharedDirty之间的区别。。。现在,乐趣开始了。

Android(以及一般的Linux系统)中的大量内存实际上是在多个进程中共享的。因此,一个进程使用多少内存还不清楚。再加上对磁盘的分页(更不用说我们在Android上不使用的交换),这就更不清楚了。

因此,如果您将实际映射到每个进程中的所有物理RAM相加,您可能会得到一个比实际总RAM大得多的数字。

Pss数是内核计算的一个度量,它考虑了内存共享——基本上,一个进程中的每一个RAM页面都是由使用该页面的其他进程数量的比率来缩放的。通过这种方式,您可以(理论上)将所有进程的pss相加,以查看它们正在使用的总RAM,并比较进程之间的pss,以大致了解它们的相对权重。

这里另一个有趣的度量是PrivateVirtualy,它基本上是进程内部无法分页到磁盘的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是特定堆的地址空间中的总大小,allocated是堆认为它拥有的实际分配的kb,free是堆剩余的kb空闲空间,用于额外分配,pss和priv-dirty与前面讨论的相同,具体到与每个堆相关联的页面。

如果您只想查看所有进程的内存使用情况,可以使用adbshellprocrank命令。同一系统上的输出如下:

  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

在这里,Vss和Rss列基本上是噪声(这是一个进程的直接地址空间和RAM使用情况,如果将进程之间的RAM使用情况相加,就会得到一个荒谬的大数字)。

PS是我们以前见过的,US是Priv Dirty。

这里值得注意的是:Pss和Us与我们在meminfo中看到的略有不同。为什么?Well procrank使用与meminfo不同的内核机制来收集数据,并且它们给出的结果略有不同。为什么?老实说,我一点线索也没有。我相信procrank可能更准确。。。但事实上,这只是离开了重点:“用一粒盐来获取任何记忆信息;通常是一粒很大的盐。”

最后是命令adbshellcat/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消耗过多而导致此类分页之前被杀死。

是的,您可以通过编程方式获取内存信息,并决定是否执行内存密集型工作。

通过调用获取VM堆大小:

Runtime.getRuntime().totalMemory();

通过调用获取分配的VM内存:

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

通过调用获取VM堆大小限制:

Runtime.getRuntime().maxMemory()

通过调用获取本机分配的内存:

Debug.getNativeHeapAllocatedSize();

我制作了一个应用程序来计算OutOfMemoryError行为并监控内存使用情况。

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

您可以在https://github.com/coocood/oom-research

上面有很多答案,肯定会对你有所帮助,但(在对adb内存工具进行了2天的负担和研究之后)我想我也可以对我的观点有所帮助。

正如Hackbot所说:因此,如果你将所有实际映射到每个进程中的物理RAM相加,你可能会得到一个比实际总RAM大得多的数字。因此,无法获得每个进程的确切内存量。

但你可以通过某种逻辑接近它。。我会告诉你。。

有一些API,如上面提到的android.os.Debug.MemoryInfo和ActivityManager.getMemoryInfo(),您可能已经阅读并使用了这些API,但我将讨论其他方式

因此,首先你需要成为一个根用户才能让它工作。通过在进程中执行su,以root权限进入控制台,并获取其输出和输入流。然后在输出流中传递id\n(enter)并将其写入进程输出。如果将获得包含uid=0的输入流,则表示您是root用户。

下面是您将在上述过程中使用的逻辑

当您获得进程的输出流时,请使用命令(procrank、dumpsys meminfo等…)而不是id,并获取其输入流并读取,以字节[]、字符[]等形式存储流。使用原始数据。。你完成了!!!!!

许可:

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

检查您是否为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
   } 
}

使用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();
 }
}

日志目录:

您可以从控制台获取单个字符串中的原始数据,而不是在某些情况下从任何API获取原始数据,这很难存储,因为您需要手动将其分离。

这只是一个尝试,如果我错过了什么,请建议我

这是一项正在进行的工作,但我不明白:

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数组?