如何在 Android 中发现我的应用程序的内存使用情况?

2022-08-31 04:04:37

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

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


答案 1

请注意,在Linux等现代操作系统上使用内存是一个非常复杂且难以理解的领域。事实上,你真正正确解释你得到的任何数字的机会都非常低。(几乎每次我和其他工程师一起查看内存使用量时,总会有关于它们实际含义的长时间讨论,这只会导致一个模糊的结论。

注意:我们现在有更多关于管理应用程序内存的文档,涵盖了此处的大部分材料,并且与Android的状态更加同步。

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

从 Android 2.0 开始的服务 API 更改

现在是我们用于查看整体内存使用情况的最高级别的 API。这主要是为了帮助应用程序衡量系统与没有更多内存用于后台进程的距离,因此需要开始杀死所需的进程(如服务)。对于纯Java应用程序,这应该没有什么用处,因为Java堆限制部分是为了避免一个应用程序能够给系统带来压力。ActivityManager.getMemoryInfo()

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

请注意,从 2.0 开始,还有一个 API,用于获取有关另一个进程的信息:ActivityManager.getProcessMemoryInfo(int[])ActivityManager.getProcessMemoryInfo

这将返回包含所有这些数据的低级 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上不使用的交换),它就更不清楚了。

因此,如果您要将所有实际映射到每个进程的物理RAM相加,并将所有进程相加,则最终可能会得到一个远远大于实际总RAM的数字。

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

这里另一个有趣的指标是 ,它基本上是进程内无法分页到磁盘的RAM量(它不受磁盘上相同数据的支持),并且不与任何其他进程共享。另一种看待这个问题的方法是,当该进程消失时,RAM将可供系统使用(并且可能很快被纳入缓存和它的其他用途中)。PrivateDirty

这几乎是为此提供的SDK API。但是,作为开发人员,您可以使用设备做更多的事情。

使用 ,您可以获得有关正在运行的系统的内存使用情况的大量信息。一个常见的命令是命令,它将吐出一堆有关每个Java进程的内存使用情况的信息,其中包含上述信息以及各种其他内容。您还可以附加单个进程的名称或pid以查看,例如,给我系统进程:adbadb shell dumpsys meminfoadb 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

顶部是主要部分,其中是特定堆的地址空间的总大小,是堆认为它具有的实际分配的kb,是堆用于其他分配的剩余可用kb,并且与之前讨论的特定于每个堆关联的页面相同。sizeallocatedfreepsspriv 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

这里的 和 列基本上是噪声(这些是进程的直接地址空间和 RAM 使用情况,如果将跨进程的 RAM 使用量相加,则会得到一个可笑的大数字)。VssRss

Pss正如我们之前所看到的,并且是.UssPriv Dirty

有趣的是,这里要注意的是:并且与我们在 中看到的略有不同(或略多)。为什么?Well procrank使用与以前不同的内核机制来收集其数据,并且它们给出的结果略有不同。为什么?老实说,我没有线索。我相信可能是更准确的一个...但实际上,这只是留下了一点:“把你得到的任何记忆信息都用一粒盐;通常是非常大的谷物。PssUssmeminfomeminfoprocrank

最后是一个命令,它提供了系统整体内存使用情况的摘要。这里有很多数据,只有前几个值得讨论的数字(剩下的几个人理解,我这几个人关于它们的问题经常导致相互矛盾的解释):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被它们消耗太多导致此类分页之前杀死后台进程。


答案 2

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

通过调用获取 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 获取源代码