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

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

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

335028 次浏览

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堆限制的存在部分是为了避免一个应用程序能够将系统压力压到这一点。

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

注意从2.0开始,还有一个APIActivityManager.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给我系统进程:

** MEMINFO in pid 890 [system] **native   dalvik    other    totalsize:    10940     7047      N/A    17987allocated:     8943     5516      N/A    14459free:      336     1531      N/A     1867(Pss):     4585     9282    11916    25783(shared dirty):     2184     3596      916     6696(priv dirty):     4504     5956     7456    17916
ObjectsViews:      149        ViewRoots:        4AppContexts:       13       Activities:        0Assets:        4    AssetManagers:        4Local Binders:      141    Proxy Binders:      158Death Recipients:       49OpenSSL Sockets:        0
SQLheap:      205          dbFiles:        0numPagers:        0   inactivePageKB:        0activePageKB:        0

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

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

PID      Vss      Rss      Pss      Uss  cmdline890   84456K   48668K   25850K   21284K  system_server1231   50748K   39088K   17587K   13792K  com.android.launcher2947   34488K   28528K   10834K    9308K  com.android.wallpaper987   26964K   26956K    8751K    7308K  com.google.process.gapps954   24300K   24296K    6249K    4824K  com.android.phone948   23020K   23016K    5864K    4748K  com.android.inputmethod.latin888   25728K   25724K    5774K    3668K  zygote977   24100K   24096K    5667K    4340K  android.process.acore...59     336K     332K      99K      92K  /system/bin/installd60     396K     392K      93K      84K  /system/bin/keystore51     280K     276K      74K      68K  /system/bin/servicemanager54     256K     252K      69K      64K  /system/bin/debuggerd

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

Pss是我们以前见过的,UssPriv Dirty

这里要注意的有趣的事情是:PssUss与我们在meminfo中看到的略有(或超过略有)不同。为什么会这样呢?ProCrank使用与meminfo不同的内核机制来收集数据,它们给出的结果略有不同。这是为什么?老实说,我不知道。我相信procrank可能更准确……但实际上,这只是离开了重点:“对你得到的任何内存信息持半信半疑的态度;通常是非常大的颗粒。”

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

MemTotal:         395144 kBMemFree:          184936 kBBuffers:             880 kBCached:            84104 kBSwapCached:            0 kB

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

MemFree是完全未使用的RAM量。您在此处看到的数字非常高;通常在Android系统上,这将只有几MB,因为我们尝试使用可用内存来保持进程运行

Cached是用于文件系统缓存和其他类似的东西的RAM。典型的系统需要有20MB左右,以避免进入糟糕的分页状态;Android内存溢出杀手针对特定系统进行了调整,以确保后台进程在缓存的RAM被它们消耗过多而导致这种分页之前被杀死。

这是一个半成品,但这是我不明白的:

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");}}

显然你想让结果数据有意义,那么为什么Google让结果关联起来这么困难呢?如果我想处理整个内存使用情况,当前的系统甚至不能正常工作,因为返回的结果是一个android.os.Debug. MemoryInfo对象的数组,但是这些对象都没有真正告诉你它们关联的是什么pid。如果你只是传入一个由所有pid组成的数组,你将无法理解结果。据我所知,它的用法是,一次传入多个pid是没有意义的,如果是这样,为什么activityManager.getProcessMemoryInfo()只接受一个int数组?

Hackbod是Stack Overflow上最好的答案之一。它揭示了一个非常模糊的主题。它对我帮助很大。

另一个非常有用的资源是这个必看视频:Google I/O 2011:Android应用程序的内存管理


更新:

进程统计,一项用于发现您的应用程序如何管理内存的服务,Dianne Hackborn在博客文章进程统计:了解您的应用程序如何使用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

Android Studio 0.8.10+引入了一个非常有用的工具,称为内存监视器

在此处输入图片描述

它有什么好处:

  • 在图表中显示可用和使用的内存,以及垃圾回收机制随着时间的推移
  • 快速测试应用程序速度是否可能会变慢与垃圾回收机制有关
  • 快速测试应用程序崩溃是否可能与运行内存溢出有关。

在此处输入图片描述

图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()-完成了这项工作,但它太慢了。一次调用大约需要200msnexus6。性能开销使这个函数对我们毫无用处,因为我们定期调用它,而且每次调用都很明显(见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指标。您可以在此处找到有关它的更多详细信息:一个两个


P. S.我注意到主题仍然缺乏一个实际而简单的代码片段,说明如果性能不是关键要求,如何估计进程的私有内存使用:

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;

上面有很多答案肯定会帮助你,但(经过2天的负担和研究亚行内存工具)我想我也可以帮助我的意见

由于Hackbod说:因此,如果您将实际映射到每个进程的所有物理RAM,并将所有进程加起来,您最终可能会得到一个比实际总RAM大得多的数字。 ,因此您无法获得每个进程的确切内存量。

但是你可以通过一些逻辑来接近它…我会告诉你如何…

有一些API,如上面提到的android.os.Debug.MemoryInfoActivityManager.getMemoryInfo(),您可能已经阅读和使用过,但我会谈论其他方式

因此,首先您需要成为root用户才能使其工作。通过在进程中执行su并获得其output and input stream以root权限进入控制台。然后在ouputstream中传递id\n(输入)并将其写入进程输出,如果将获得包含uid=0您是root用户。的输入流

现在这里是逻辑您将使用在上面的过程

当你得到进程用#0而不是id传递你的命令(procrans,dumsters meminfo等…)的输出流并得到它的inputstream并读取时,将流存储在bytes[]、char[]等…使用原始数据…你就完成了!!!!!

权限:

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

检查您是否是root用户:

// su command to get root accessProcess 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 enterdataOutputStream.writeBytes("id\n");dataOutputStream.flush();String Uid = dataInputStream.readLine();// read output and check if uid is thereif (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 commanddataOutputStream.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 streamtry {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();}}

logcat:结果

您可以从控制台获得单个字符串中的原始数据,而不是在某些实例中从任何API获取,这对于存储来说很复杂,因为您需要手动将其分开

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

在android Studio 3.0中,他们引入了android-分析器来帮助您了解您的应用程序如何使用CPU、内存、网络和电池资源。

https://developer.android.com/studio/profile/android-profiler

在此处输入图片描述

我在阅读答案时感到困惑,所以我决定阅读文档。好的,我们开始吧:

本机堆内存使用情况

您可以使用调试对象获取设备的本机堆内存大小:

long nativeTotal = Debug.getNativeHeapSize();long nativeFree = Debug.getNativeHeapFreeSize();long nativeAllocated = Debug.getNativeHeapAllocatedSize();long nativeUsed = nativeTotal - nativeFree;

注意:nativeUsednativeAllocated具有相同的值。

运行时内存使用情况

使用应用程序运行时对象的内存使用情况,每个应用程序都有一个运行时对象来访问其接口:

Runtime runtime = Runtime.getRuntime();long runtimeMax = runtime.maxMemory();long runtimeTotal = runtime.totalMemory();long runtimeFree = runtime.freeMemory();long runtimeUsed = runtimeTotal - runtimeFree;

系统内存使用

您可以使用活动管理器。内存信息对象获取系统的内存使用情况:

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();activityManager.getMemoryInfo(memoryInfo);long systemTotal = memoryInfo.totalMem;long systemFree = memoryInfo.availMem;long systemUsed = systemTotal - systemFree;

proc内存使用

另一种读取系统内存使用情况的方法是在linux中解析/proc/meminfo文件的内容。

RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");Pattern pattern = Pattern.compile("(\\d+)");Matcher totalMatcher = pattern.matcher(reader.readLine());totalMatcher.find();long procTotal = Long.parseLong(totalMatcher.group(1)) * 1024L; // KB to BMatcher freeMatcher = pattern.matcher(reader.readLine());freeMatcher.find();long procFree = Long.parseLong(freeMatcher.group(1)) * 1024L; // KB to Blong procUsed = procTotal - procFree;

好的,我还是很困惑。但是这是你可以得到内存使用情况的所有方法。将它们全部记录下来以调试您的应用程序!!!

我还为他们每个人提供了一个链接,以阅读他们的使用情况和信息。