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

新手上路,请多包涵

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

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

原文由 Andrea Baccega 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 681
2 个回答

请注意,现代操作系统(如 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 上不使用的交换),它就更不清楚了。

因此,如果您要获取实际映射到每个进程的所有物理 RAM,并将所有进程加起来,您最终可能会得到比实际总 RAM 大得多的数字。

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

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

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

使用 adb ,您可以获得很多关于正在运行的系统的内存使用情况的信息。一个常见的是命令 adb shell dumpsys meminfo 它将吐出一堆关于每个 Java 进程的内存使用信息,包含上述信息以及各种其他信息。你也可以加上单个进程的名称或pid来查看,例如 adb shell dumpsys meminfo system 给我系统进程:

** pid 890 [系统] 中的 MEMINFO **
                    native dalvik 其他合计
            尺寸:10940 7047 不适用 17987
       分配:8943 5516 N/A 14459
            免费:336 1531 不适用 1867
           (PSS): 4585 9282 11916 25783
  (共享脏):2184 3596 916 6696
    (私人脏):4504 5956 7456 17916

 对象
           意见:149 ViewRoots:4
     AppContexts:13 活动:0
          资产:4 资产管理者:4
   本地活页夹:141 代理活页夹:158
死亡接受者:49
 OpenSSL 套接字:0

 数据库
            堆:205 dbFiles:0
       numPagers:0 inactivePageKB:0
    活动页面KB:0

顶部是主要部分,其中 size 是特定堆地址空间的总大小, allocated 是堆认为它拥有的实际分配的 kb, free 是剩余的可用 kb 堆有额外的分配,和 psspriv dirty 与之前讨论的每个特定于堆的页面相同。

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

  PID Vss Rss Pss Uss cmdline
  890 84456K 48668K 25850K 21284K 系统服务器
 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 合子
  977 24100K 24096K 5667K 4340K android.process.acore
...
   59 336K 332K 99K 92K /system/bin/installd
   60 396K 392K 93K 84K /系统/bin/密钥库
   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 可能是更准确的……但实际上,这只留下了重点:“对你获得的任何内存信息持保留态度;通常是非常大的一粒盐。”

最后是命令 adb shell cat /proc/meminfo 给出了系统总体内存使用情况的摘要。这里有很多数据,值得讨论的只有前几个数字(其余的很少有人理解,而且我对那几个人的问题经常导致相互矛盾的解释):

内存总数:395144 kB
MemFree:184936 kB
缓冲区:880 kB
缓存:84104 kB
交换缓存:0 kB

MemTotal 是内核和用户空间可用的内存总量(通常小于设备的实际物理 RAM,因为无线电、DMA 缓冲区等需要一些 RAM)。

MemFree 是根本没有使用的内存量。你在这里看到的数字非常高;通常在 Android 系统上这将只有几 MB,因为我们尝试使用可用内存来保持进程运行

Cached 是用于文件系统缓存和其他类似事物的 RAM。典型的系统需要有 20MB 左右的空间来避免进入错误的分页状态; Android 内存不足杀手针对特定系统进行了调整,以确保后台进程在缓存 RAM 被消耗过多以导致此类分页之前被杀死。

原文由 hackbod 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

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

原文由 coocood 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题