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

我希望有办法做到这一点.另外,我怎样才能获得手机的免费内存呢?

推荐答案

请注意,Linux等现代操作系统上的内存使用是一个复杂且难以理解的领域.事实上,无论你得到什么数字,你真正正确解读的几率都非常低.(几乎每次我和其他工程师一起查看内存使用率数据时,都会对它们的实际含义进行长时间的讨论,只会得出一个模糊的结论.)

Note: we now have much more extensive documentation on 100 that covers much of the material here and is more up-to-date with the state of Android.

第一件事可能是阅读本文的最后部分,其中讨论了Android上内存是如何管理的:

Service API changes starting with Android 2.0

现在,ActivityManager.getMemoryInfo()是我们查看总体内存使用情况的最高级别API.这主要是为了帮助应用程序判断系统即将失go 用于后台进程的内存,因此需要开始终止所需的进程,比如服务.对于纯Java应用程序来说,这应该没有什么用处,因为Java堆限制的存在部分是为了避免一个应用程序将系统压力压到这一点.

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

Note starting with 2.0 there is also an API, ActivityManager.getProcessMemoryInfo, to get this information about another process: ActivityManager.getProcessMemoryInfo(int[])

这将返回一个包含所有这些数据的低级MemoryInfo struct :

    /** 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都是按照同样使用该页的其他进程的数量的比率进行zoom 的.通过这种方式,你可以(理论上)将所有进程的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是特定堆的地址空间中的总大小,allocated是堆认为它拥有的实际分配的kb,free是堆为额外分配剩余的kb,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中看到的略有不同(或略有不同).为什么?Procrack使用了与meminfo不同的内核机制来收集数据,它们给出的结果略有不同.为什么?老实说,我一点也不知道.我相信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量.你在这里看到的数字非常高;通常在安卓系统上,这只有几MB,因为我们试图使用可用内存来保持进程运行

Cached是用于文件系统缓存和其他类似事情的RAM.典型的系统需要20MB左右,以避免进入糟糕的分页状态;Android out of memory killer针对特定系统进行了调整,以确保后台进程在缓存的RAM被过度消耗而导致此类分页之前被终止.

Java相关问答推荐

Collections.binarySearch()使用Collections.reverseOrder()作为比较器返回-1

查找最大子数组的和

即使我正在使用并发方法,使用Javascript的应用程序也会继续冻结'

Jooq外键关系

Java中如何根据Font.canDisplay方法对字符串进行分段

R.id.main给我一个红色错误,无法解析MainActivity.java中的符号main

当涉及到泛型时,类型推理在Java中是如何工作的?

尽管通过中断请求线程死亡,但线程仍将继续存在

如何为JavaFX Spring Boot应用程序制作Windows/MacOS/Linux安装程序

在Eclipse中调试未导出的JDK模块的Java包

在macOS上读取文件会导致FileNotFound,即使文件存在(并且具有权限)

垃圾回收器是否真的删除超出作用域的对象?

如何创建模块信息类文件并将其添加到JAR中?

带有可选部分的Java DateTimeForMatter

为什么相同的数据条码在视觉上看起来不同?

如何使用Java对随机生成的字母数字优惠券代码进行过期设置

无法在Java中获取ElastiCache的AWS CloudWatch指标

将基于实例编号的对象列表拆分为新的对象列表

将@Transactional添加到Spring框架中链下的每个方法会产生什么效果?

Java中计算大n和k值模10^9+7的二项式系数的乘法公式输出错误值