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

我怎样才能find我的Android应用程序使用的内存,编程?

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

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

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

首先可能会读到这篇文章的最后一部分,它讨论了如何在Android上pipe理内存:

服务API更改从Android 2.0开始

现在ActivityManager.getMemoryInfo()是我们用于查看整个内存使用情况的最高级别的API。 这主要是为了帮助应用程序衡量系统到后台进程没有更多内存需要多长时间,因此需要开始查杀所需的进程(如服务)。 对于纯Java应用程序,这应该是没有用的,因为Java堆的限制在一定程度上是为了避免一个应用程序能够强调系统到这一点。

在较低级别上,您可以使用debuggingAPI来获取有关内存使用情况的原始内核级信息: 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系统)中的大量内存实际上是在多个进程之间共享的。 那么一个进程使用多less内存真的不清楚。 添加到磁盘的页面顶部(更不用说在Android上不使用的交换),而且更加不清楚。

因此,如果将所有实际映射到每个进程的物理RAM并将所有进程相加,则最终可能会比实际的总RAM大得多。

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

另一个有趣的指标是PrivateDirty ,它基本上是进程内的RAM数量,不能被分页到磁盘(它不被磁盘上的相同数据支持),并且不与其他进程共享。 另一种看待这个问题的方法是当这个进程消失时(也可能很快包含到caching和其他用途中),系统可以使用的RAM。

这几乎是这个SDK的API。 但是,您可以使用设备作为开发者进行更多操作。

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

 ** MEMINFO在pid 890 [系统] **
                    原生dalvik其他总数
            尺寸:10940 7047 N / A 17987
       分配:8943 5516 N / A 14459
            免费:336 1531 N / A 1867
            (PSS):4585 9282 11916 25783
   (共享脏):2184 3596 916 6696
     (priv脏):4504 5956 7456 17916

 对象
           查看次数:149 ViewRoots:4
      AppContexts:13活动:0
          资产:4个资产pipe理者:4
   本地绑定器:141代理绑定器:158
死亡的收件人:49
  OpenSSL套接字:0

  SQL
            堆: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可能是更准确的一个……但实际上,这只是留下了这样一个观点:“记下你得到的任何记忆信息,通常是一粒非常大的谷粒。

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

 MemTotal:395144 kB
 MemFree:184936 kB
缓冲区:880 kB
caching:84104 kB
 SwapCached:0 kB

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

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

Cached是用于文件系统caching和其他类似的东西的RAM。 典型的系统需要20MB左右,以避免进入坏的寻呼状态; Android内存不足杀手会针对特定的系统进行调整,以确保后台进程在高速caching的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获取源代码;

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

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

Hackbod的是堆栈溢出最好的答案之一。 它揭示了一个非常模糊的主题。 它帮了我很多。

另一个真正有用的资源是这个必看video: Google I / O 2011:Android应用程序的内存pipe理


更新:

过程统计,一种服务,发现你的应用程序如何pipe理内存解释在博客文章过程统计:了解你的应用程序如何使用内存 Dianne Hackborn:

1)我猜不是,至less不是来自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+引入了一个令人难以置信的有用的工具称为内存监视器 。

在这里输入图像描述

它有什么好处:

  • 显示图中可用和已用的内存,以及随着时间的推移垃圾收集事件。
  • 快速testing应用程序缓慢是否与过度的垃圾回收事件有关。
  • 快速testing应用程序崩溃可能与内存不足有关。

在这里输入图像描述

图1.在Android内存监视器上强制GC(垃圾收集)事件

通过使用它,您可以获得关于应用程序RAM实时消耗的大量信息。

我们发现,获得当前进程总内存的所有标准方法都存在一些问题。

  • 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度量。 你可以在这里find更多的细节: 一 , 二和三 。


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;