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

时间:2022-02-11 07:29:20

How can I find the memory used on my Android application, programmatically?

如何找到Android应用程序中使用的内存?

I hope there is a way to do it. Plus, how do I get the free memory of the phone too?

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

8 个解决方案

#1


967  

Note that memory usage on modern operating systems like Linux is an extremely complicated and difficult to understand area. In fact the chances of you actually correctly interpreting whatever numbers you get is extremely low. (Pretty much every time I look at memory usage numbers with other engineers, there is always a long discussion about what they actually mean that only results in a vague conclusion.)

注意,在现代操作系统(如Linux)上的内存使用是一个极其复杂和难以理解的领域。事实上,无论你得到什么数字,你正确解读的几率都非常低。(几乎每次我和其他工程师一起查看内存使用数字时,总会有一大堆关于它们的真正含义的讨论,而这些讨论只会得出一个模糊的结论。)

Note: we now have much more extensive documentation on Managing Your App's Memory that covers much of the material here and is more up-to-date with the state of Android.

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

First thing is to probably read the last part of this article which has some discussion of how memory is managed on Android:

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

Service API changes starting with Android 2.0

服务API从Android 2.0开始改变

Now ActivityManager.getMemoryInfo() is our highest-level API for looking at overall memory usage. This is mostly there to help an application gauge how close the system is coming to having no more memory for background processes, thus needing to start killing needed processes like services. For pure Java applications, this should be of little use, since the Java heap limit is there in part to avoid one app from being able to stress the system to this point.

现在,ActivityManager.getMemoryInfo()是查看总体内存使用情况的*API。这主要是为了帮助应用程序判断系统离后台进程没有内存有多近,因此需要开始杀死需要的进程,比如服务。对于纯Java应用程序来说,这应该没什么用,因为Java堆限制在一定程度上是为了避免一个应用程序在这一点上对系统造成压力。

Going lower-level, you can use the Debug API to get raw kernel-level information about memory usage: android.os.Debug.MemoryInfo

在更低的级别上,您可以使用调试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[])

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

This returns a low-level MemoryInfo structure with all of this data:

它返回一个具有所有这些数据的低级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;

But as to what the difference is between Pss, PrivateDirty, and SharedDirty... well now the fun begins.

但是,Pss、privatrty和SharedDirty之间有什么区别呢?现在有趣的事情开始了。

A lot of memory in Android (and Linux systems in general) is actually shared across multiple processes. So how much memory a processes uses is really not clear. Add on top of that paging out to disk (let alone swap which we don't use on Android) and it is even less clear.

Android(通常是Linux系统)中的许多内存实际上是跨多个进程共享的。因此,进程使用了多少内存还不清楚。再加上对磁盘的分页(更不用说在Android上不使用的交换),就更不清楚了。

Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM.

因此,如果要将所有物理RAM都映射到每个进程,并将所有进程相加,那么最终可能会得到比实际RAM更大的数字。

The Pss number is a metric the kernel computes that takes into account memory sharing -- basically each page of RAM in a process is scaled by a ratio of the number of other processes also using that page. This way you can (in theory) add up the pss across all processes to see the total RAM they are using, and compare pss between processes to get a rough idea of their relative weight.

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

The other interesting metric here is PrivateDirty, which is basically the amount of RAM inside the process that can not be paged to disk (it is not backed by the same data on disk), and is not shared with any other processes. Another way to look at this is the RAM that will become available to the system when that process goes away (and probably quickly subsumed into caches and other uses of it).

这里另一个有趣的指标是PrivateDirty,它基本上是进程中不能被分页到磁盘的RAM数量(它不受磁盘上相同数据的支持),并且不与任何其他进程共享。另一种方式是当进程结束时,系统可以使用的RAM(可能很快被包含到缓存和其他使用中)。

That is pretty much the SDK APIs for this. However there is more you can do as a developer with your device.

这就是SDK api。然而,作为一个开发人员,你可以用你的设备做更多的事情。

Using adb, there is a lot of information you can get about the memory use of a running system. A common one is the command adb shell dumpsys meminfo which will spit out a bunch of information about the memory use of each Java process, containing the above info as well as a variety of other things. You can also tack on the name or pid of a single process to see, for example adb shell dumpsys meminfo system give me the system process:

使用adb,您可以获得关于运行系统的内存使用的许多信息。一个常见的方法是命令adb shell dumpsys meminfo,它将给出关于每个Java进程的内存使用的大量信息,其中包含上述信息以及其他各种信息。您也可以在单个进程的名称或pid上添加查看,例如adb shell dumpsys meminfo系统给我系统进程:

** 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

The top section is the main one, where size is the total size in address space of a particular heap, allocated is the kb of actual allocations that heap thinks it has, free is the remaining kb free the heap has for additional allocations, and pss and priv dirty are the same as discussed before specific to pages associated with each of the heaps.

上面部分是主要的一个,大小是总堆大小在一个特定的地址空间,分配的kb实际分配堆认为,*是其余kb*堆额外拨款,pss和priv脏之前讨论特定于页面是一样的与每一个堆。

If you just want to look at memory usage across all processes, you can use the command adb shell procrank. Output of this on the same system looks like:

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

  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

Here the Vss and Rss columns are basically noise (these are the straight-forward address space and RAM usage of a process, where if you add up the RAM usage across processes you get an ridiculously large number).

在这里,Vss和Rss列基本上都是噪音(这些是进程的直接地址空间和RAM使用量,如果将进程间的RAM使用量相加,就会得到一个非常大的数字)。

Pss is as we've seen before, and Uss is Priv Dirty.

Pss就像我们之前看到的,而Uss很脏。

Interesting thing to note here: Pss and Uss are slightly (or more than slightly) different than what we saw in meminfo. Why is that? Well procrank uses a different kernel mechanism to collect its data than meminfo does, and they give slightly different results. Why is that? Honestly I haven't a clue. I believe procrank may be the more accurate one... but really, this just leave the point: "take any memory info you get with a grain of salt; often a very large grain."

这里需要注意的有趣的事情是:Pss和Uss与我们在meminfo中看到的有所不同。这是为什么呢?procranky使用与meminfo不同的内核机制来收集数据,结果略有不同。这是为什么呢?老实说,我一点头绪都没有。我相信procranky可能更准确……但实际上,这就留下了一个问题:“对你得到的任何有关记忆的信息都半信半疑;通常是非常大的谷物。

Finally there is the command adb shell cat /proc/meminfo that gives a summary of the overall memory usage of the system. There is a lot of data here, only the first few numbers worth discussing (and the remaining ones understood by few people, and my questions of those few people about them often resulting in conflicting explanations):

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

MemTotal:         395144 kB
MemFree:          184936 kB
Buffers:             880 kB
Cached:            84104 kB
SwapCached:            0 kB

MemTotal is the total amount of memory available to the kernel and user space (often less than the actual physical RAM of the device, since some of that RAM is needed for the radio, DMA buffers, etc).

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

MemFree is the amount of RAM that is not being used at all. The number you see here is very high; typically on an Android system this would be only a few MB, since we try to use available memory to keep processes running

MemFree是完全不使用的RAM的数量。你在这里看到的数字很高;通常在Android系统上,这只是几个MB,因为我们尝试使用可用内存来保持进程运行。

Cached is the RAM being used for filesystem caches and other such things. Typical systems will need to have 20MB or so for this to avoid getting into bad paging states; the Android out of memory killer is tuned for a particular system to make sure that background processes are killed before the cached RAM is consumed too much by them to result in such paging.

缓存是用于文件系统缓存和其他此类事情的RAM。典型的系统需要20MB左右才能避免进入糟糕的分页状态;将Android out of memory killer调优为一个特定的系统,以确保后台进程在缓存的RAM被占用过多而导致这种分页之前被杀死。

#2


69  

Yes, you can get memory info programmatically and decide whether to do memory intensive work.

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

Get VM Heap Size by calling:

通过调用获取VM堆大小:

Runtime.getRuntime().totalMemory();

Get Allocated VM Memory by calling:

通过调用获得分配的VM内存:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Get VM Heap Size Limit by calling:

通过调用:

Runtime.getRuntime().maxMemory()

Get Native Allocated Memory by calling:

通过调用获取本机分配的内存:

Debug.getNativeHeapAllocatedSize();

I made an app to figure out the OutOfMemoryError behavior and monitor memory usage.

我做了一个应用程序来计算OutOfMemoryError行为并监视内存使用情况。

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

You can get the source code at https://github.com/coocood/oom-research

您可以通过https://github.com/coocood/oom-research获得源代码

#3


48  

This is a work in progress, but this is what I don't understand:

这是一项正在进行的工作,但这是我不理解的:

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

Why isn't the PID mapped to the result in activityManager.getProcessMemoryInfo()? Clearly you want to make the resulting data meaningful, so why has Google made it so difficult to correlate the results? The current system doesn't even work well if I want to process the entire memory usage since the returned result is an array of android.os.Debug.MemoryInfo objects, but none of those objects actually tell you what pids they are associated with. If you simply pass in an array of all pids, you will have no way to understand the results. As I understand it's use, it makes it meaningless to pass in more than one pid at a time, and then if that's the case, why make it so that activityManager.getProcessMemoryInfo() only takes an int array?

为什么PID没有映射到activityManager.getProcessMemoryInfo()中的结果?显然,您想要使结果数据有意义,那么为什么谷歌使结果变得如此困难?如果我想处理整个内存使用情况,当前系统甚至不能正常工作,因为返回的结果是一个android.os.Debug数组。MemoryInfo对象,但是没有一个对象会告诉你它们与什么pid相关联。如果您只是传递一个包含所有pid的数组,那么您将无法理解结果。正如我所理解的,每次传入多个pid是没有意义的,如果是这样,为什么要让activityManager.getProcessMemoryInfo()只接收一个int数组呢?

#4


24  

Hackbod's is one of the best answers on Stack Overflow. It throws light on a very obscure subject. It helped me a lot.

Hackbod是关于栈溢出的最佳答案之一。它阐明了一个非常模糊的问题。这对我帮助很大。

Another really helpful resource is this must-see video: Google I/O 2011: Memory management for Android Apps

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


UPDATE:

更新:

Process Stats, a service to discover how your app manages memory explained at the blog post Process Stats: Understanding How Your App Uses RAM by Dianne Hackborn:

Process Stats,一个发现你的应用如何管理内存的服务,在博客文章Process Stats中解释:理解你的应用如何使用RAM, Dianne Hackborn:

#5


19  

Android Studio 0.8.10+ has introduced an incredibly useful tool called Memory Monitor.

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

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

What it's good for:

什么是好的:

  • Showing available and used memory in a graph, and garbage collection events over time.
  • 在图表中显示可用内存和使用的内存,以及随着时间的推移发生的垃圾收集事件。
  • Quickly testing whether app slowness might be related to excessive garbage collection events.
  • 快速测试应用程序的慢度是否与过度的垃圾收集事件有关。
  • Quickly testing whether app crashes may be related to running out of memory.
  • 快速测试应用程序崩溃是否与内存耗尽有关。

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

Figure 1. Forcing a GC (Garbage Collection) event on Android Memory Monitor

图1所示。在Android内存监视器上强制执行GC(垃圾收集)事件

You can have plenty good information on your app's RAM real-time consumption by using it.

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

#6


16  

1) I guess not, at least not from Java.
2)

1)我想没有,至少Java没有。

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

#7


2  

We found out that all the standard ways of getting the total memory of the current process have some issues.

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

  • Runtime.getRuntime().totalMemory(): returns JVM memory only
  • getruntime ().totalMemory():只返回JVM内存
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() and anything else based on /proc/meminfo - returns memory info about all the processes combined (e.g. android_util_Process.cpp)
  • getmemoryinfo()、Process.getFreeMemory()以及任何基于/proc/meminfo的东西——返回所有进程的内存信息(例如android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - uses mallinfo() which return information about memory allocations performed by malloc() and related functions only (see android_os_Debug.cpp)
  • getnativeheapallocatedsize()——使用mallinfo(),它只返回关于malloc()执行的内存分配和相关函数的信息(参见android_os_Debug.cpp)
  • Debug.getMemoryInfo() - does the job but it's too slow. It takes about 200ms on Nexus 6 for a single call. The performance overhead makes this function useless for us as we call it regularly and every call is quite noticeable (see android_os_Debug.cpp)
  • getmemoryinfo()—完成这个任务,但是太慢了。Nexus 6的一次通话大约需要200毫秒。性能开销使得这个函数对我们来说毫无用处,因为我们经常调用它,并且每次调用都很明显(参见android_os_Debug.cpp)
  • ActivityManager.getProcessMemoryInfo(int[]) - calls Debug.getMemoryInfo() internally (see ActivityManagerService.java)
  • -在内部调用Debug.getMemoryInfo()

Finally, we ended up using the following code:

最后,我们使用了以下代码:

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;

It returns VmRSS metric. You can find more details about it here: one, two and three.

它返回VmRSS度量。你可以在这里找到更多的细节:1、2和3。


P.S. I noticed that the theme still has a lack of an actual and simple code snippet of how to estimate the private memory usage of the process if the performance isn't a critical requirement:

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;

#8


0  

There are a lot of answer above which will definitely help you but i (after 2 days of afford and research on adb memory tools) think i can help with my opinion too.

上面有很多答案肯定会对您有所帮助,但是我(在花了两天的时间研究adb内存工具之后)认为我也可以帮助您理解我的观点。

As Hackbod says : Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM. so there is no way you can get exact amount of memory per process.

正如Hackbod所言:因此,如果您将实际映射到每个进程的所有物理RAM,并将所有进程相加,您可能会得到一个比实际的总RAM大得多的数字。因此,您不可能获得每个进程的确切内存数量。

But you can get close to it by some logic..and I will tell how..

但是你可以用某种逻辑来接近它。我会告诉你…

There are some API like android.os.Debug.MemoryInfo and ActivityManager.getMemoryInfo() mentioned above which you already might have being read about and used but I will talk about another way (indirect one)

有一些API像android.os.Debug。上面提到的MemoryInfo和activitymanager。getmemoryinfo()你们可能已经读过并使用过了但我将用另一种方式(间接的)

So firstly you need to be a root user to get it work. Get into console with root privilege by executing su in process and get its output and input stream.Then pass id\n (enter) in ouputstream and write it to process output, If will get an inputstream containing uid=0, you are root user.

首先,你需要成为一个根用户来让它工作。通过在进程中执行su,获得控制台的根特权,并获得其输出和输入流。然后在ouputstream中传递id\n (enter),并将其写入进程输出,如果得到包含uid=0的inputstream,那么您就是根用户。

Now here is the logic which you will use in above process

这就是你将在上面的过程中用到的逻辑

When you get ouputstream of process pass you command (procrank, dumpsys meminfo etc...) with \n instead of id and get its inputstream and read, store the stream in bytes,char etc.. use raw data..and you are done!!!!!

当你获得进程的输出流通过你的命令(procranke, dumpsys meminfo等)时,用\n代替id,获取它的inputstream并读取,以字节、char等形式存储流。使用原始数据. .和你做! ! ! ! !

permission :

许可:

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

Check if you are root user :

检查您是否是根用户:

try {
    // su command to get root access
    Process 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 enter
        dataOutputStream.writeBytes("id\n");                   
        dataOutputStream.flush();
        String Uid = dataInputStream.readLine();
        // read output and check if uid is there
        if (Uid.contains("uid=0")) {                           
             // yes sir you are root user
         } 
    }
} catch (Exception e) {
}

Execute your command with su

使用su执行命令

try {
      // su
      Process process = Runtime.getRuntime().exec("su");         
      DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream());
      if (dataOutputStream != null) {
          // adb command
          dataOutputStream.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 stream 
          try {
              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();
      }
} catch (Exception e) {
}

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

logcat:

This is not a solution as you wont get data in some instance or from any API, instead you get a raw data in a single string just as you get from console, which is very hard to manage and complex to store as you will need to separate it manually.

这不是一个解决方案,你不会得到一些实例中的数据或从任何API,而不是得到一个字符串的原始数据就像你从控制台,这是非常难以管理和复杂的存储需要单独的手动。

This is just a try, please correct me if I am wrong

这只是一个尝试,如果我错了请纠正我

#1


967  

Note that memory usage on modern operating systems like Linux is an extremely complicated and difficult to understand area. In fact the chances of you actually correctly interpreting whatever numbers you get is extremely low. (Pretty much every time I look at memory usage numbers with other engineers, there is always a long discussion about what they actually mean that only results in a vague conclusion.)

注意,在现代操作系统(如Linux)上的内存使用是一个极其复杂和难以理解的领域。事实上,无论你得到什么数字,你正确解读的几率都非常低。(几乎每次我和其他工程师一起查看内存使用数字时,总会有一大堆关于它们的真正含义的讨论,而这些讨论只会得出一个模糊的结论。)

Note: we now have much more extensive documentation on Managing Your App's Memory that covers much of the material here and is more up-to-date with the state of Android.

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

First thing is to probably read the last part of this article which has some discussion of how memory is managed on Android:

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

Service API changes starting with Android 2.0

服务API从Android 2.0开始改变

Now ActivityManager.getMemoryInfo() is our highest-level API for looking at overall memory usage. This is mostly there to help an application gauge how close the system is coming to having no more memory for background processes, thus needing to start killing needed processes like services. For pure Java applications, this should be of little use, since the Java heap limit is there in part to avoid one app from being able to stress the system to this point.

现在,ActivityManager.getMemoryInfo()是查看总体内存使用情况的*API。这主要是为了帮助应用程序判断系统离后台进程没有内存有多近,因此需要开始杀死需要的进程,比如服务。对于纯Java应用程序来说,这应该没什么用,因为Java堆限制在一定程度上是为了避免一个应用程序在这一点上对系统造成压力。

Going lower-level, you can use the Debug API to get raw kernel-level information about memory usage: android.os.Debug.MemoryInfo

在更低的级别上,您可以使用调试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[])

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

This returns a low-level MemoryInfo structure with all of this data:

它返回一个具有所有这些数据的低级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;

But as to what the difference is between Pss, PrivateDirty, and SharedDirty... well now the fun begins.

但是,Pss、privatrty和SharedDirty之间有什么区别呢?现在有趣的事情开始了。

A lot of memory in Android (and Linux systems in general) is actually shared across multiple processes. So how much memory a processes uses is really not clear. Add on top of that paging out to disk (let alone swap which we don't use on Android) and it is even less clear.

Android(通常是Linux系统)中的许多内存实际上是跨多个进程共享的。因此,进程使用了多少内存还不清楚。再加上对磁盘的分页(更不用说在Android上不使用的交换),就更不清楚了。

Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM.

因此,如果要将所有物理RAM都映射到每个进程,并将所有进程相加,那么最终可能会得到比实际RAM更大的数字。

The Pss number is a metric the kernel computes that takes into account memory sharing -- basically each page of RAM in a process is scaled by a ratio of the number of other processes also using that page. This way you can (in theory) add up the pss across all processes to see the total RAM they are using, and compare pss between processes to get a rough idea of their relative weight.

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

The other interesting metric here is PrivateDirty, which is basically the amount of RAM inside the process that can not be paged to disk (it is not backed by the same data on disk), and is not shared with any other processes. Another way to look at this is the RAM that will become available to the system when that process goes away (and probably quickly subsumed into caches and other uses of it).

这里另一个有趣的指标是PrivateDirty,它基本上是进程中不能被分页到磁盘的RAM数量(它不受磁盘上相同数据的支持),并且不与任何其他进程共享。另一种方式是当进程结束时,系统可以使用的RAM(可能很快被包含到缓存和其他使用中)。

That is pretty much the SDK APIs for this. However there is more you can do as a developer with your device.

这就是SDK api。然而,作为一个开发人员,你可以用你的设备做更多的事情。

Using adb, there is a lot of information you can get about the memory use of a running system. A common one is the command adb shell dumpsys meminfo which will spit out a bunch of information about the memory use of each Java process, containing the above info as well as a variety of other things. You can also tack on the name or pid of a single process to see, for example adb shell dumpsys meminfo system give me the system process:

使用adb,您可以获得关于运行系统的内存使用的许多信息。一个常见的方法是命令adb shell dumpsys meminfo,它将给出关于每个Java进程的内存使用的大量信息,其中包含上述信息以及其他各种信息。您也可以在单个进程的名称或pid上添加查看,例如adb shell dumpsys meminfo系统给我系统进程:

** 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

The top section is the main one, where size is the total size in address space of a particular heap, allocated is the kb of actual allocations that heap thinks it has, free is the remaining kb free the heap has for additional allocations, and pss and priv dirty are the same as discussed before specific to pages associated with each of the heaps.

上面部分是主要的一个,大小是总堆大小在一个特定的地址空间,分配的kb实际分配堆认为,*是其余kb*堆额外拨款,pss和priv脏之前讨论特定于页面是一样的与每一个堆。

If you just want to look at memory usage across all processes, you can use the command adb shell procrank. Output of this on the same system looks like:

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

  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

Here the Vss and Rss columns are basically noise (these are the straight-forward address space and RAM usage of a process, where if you add up the RAM usage across processes you get an ridiculously large number).

在这里,Vss和Rss列基本上都是噪音(这些是进程的直接地址空间和RAM使用量,如果将进程间的RAM使用量相加,就会得到一个非常大的数字)。

Pss is as we've seen before, and Uss is Priv Dirty.

Pss就像我们之前看到的,而Uss很脏。

Interesting thing to note here: Pss and Uss are slightly (or more than slightly) different than what we saw in meminfo. Why is that? Well procrank uses a different kernel mechanism to collect its data than meminfo does, and they give slightly different results. Why is that? Honestly I haven't a clue. I believe procrank may be the more accurate one... but really, this just leave the point: "take any memory info you get with a grain of salt; often a very large grain."

这里需要注意的有趣的事情是:Pss和Uss与我们在meminfo中看到的有所不同。这是为什么呢?procranky使用与meminfo不同的内核机制来收集数据,结果略有不同。这是为什么呢?老实说,我一点头绪都没有。我相信procranky可能更准确……但实际上,这就留下了一个问题:“对你得到的任何有关记忆的信息都半信半疑;通常是非常大的谷物。

Finally there is the command adb shell cat /proc/meminfo that gives a summary of the overall memory usage of the system. There is a lot of data here, only the first few numbers worth discussing (and the remaining ones understood by few people, and my questions of those few people about them often resulting in conflicting explanations):

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

MemTotal:         395144 kB
MemFree:          184936 kB
Buffers:             880 kB
Cached:            84104 kB
SwapCached:            0 kB

MemTotal is the total amount of memory available to the kernel and user space (often less than the actual physical RAM of the device, since some of that RAM is needed for the radio, DMA buffers, etc).

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

MemFree is the amount of RAM that is not being used at all. The number you see here is very high; typically on an Android system this would be only a few MB, since we try to use available memory to keep processes running

MemFree是完全不使用的RAM的数量。你在这里看到的数字很高;通常在Android系统上,这只是几个MB,因为我们尝试使用可用内存来保持进程运行。

Cached is the RAM being used for filesystem caches and other such things. Typical systems will need to have 20MB or so for this to avoid getting into bad paging states; the Android out of memory killer is tuned for a particular system to make sure that background processes are killed before the cached RAM is consumed too much by them to result in such paging.

缓存是用于文件系统缓存和其他此类事情的RAM。典型的系统需要20MB左右才能避免进入糟糕的分页状态;将Android out of memory killer调优为一个特定的系统,以确保后台进程在缓存的RAM被占用过多而导致这种分页之前被杀死。

#2


69  

Yes, you can get memory info programmatically and decide whether to do memory intensive work.

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

Get VM Heap Size by calling:

通过调用获取VM堆大小:

Runtime.getRuntime().totalMemory();

Get Allocated VM Memory by calling:

通过调用获得分配的VM内存:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

Get VM Heap Size Limit by calling:

通过调用:

Runtime.getRuntime().maxMemory()

Get Native Allocated Memory by calling:

通过调用获取本机分配的内存:

Debug.getNativeHeapAllocatedSize();

I made an app to figure out the OutOfMemoryError behavior and monitor memory usage.

我做了一个应用程序来计算OutOfMemoryError行为并监视内存使用情况。

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

You can get the source code at https://github.com/coocood/oom-research

您可以通过https://github.com/coocood/oom-research获得源代码

#3


48  

This is a work in progress, but this is what I don't understand:

这是一项正在进行的工作,但这是我不理解的:

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

Why isn't the PID mapped to the result in activityManager.getProcessMemoryInfo()? Clearly you want to make the resulting data meaningful, so why has Google made it so difficult to correlate the results? The current system doesn't even work well if I want to process the entire memory usage since the returned result is an array of android.os.Debug.MemoryInfo objects, but none of those objects actually tell you what pids they are associated with. If you simply pass in an array of all pids, you will have no way to understand the results. As I understand it's use, it makes it meaningless to pass in more than one pid at a time, and then if that's the case, why make it so that activityManager.getProcessMemoryInfo() only takes an int array?

为什么PID没有映射到activityManager.getProcessMemoryInfo()中的结果?显然,您想要使结果数据有意义,那么为什么谷歌使结果变得如此困难?如果我想处理整个内存使用情况,当前系统甚至不能正常工作,因为返回的结果是一个android.os.Debug数组。MemoryInfo对象,但是没有一个对象会告诉你它们与什么pid相关联。如果您只是传递一个包含所有pid的数组,那么您将无法理解结果。正如我所理解的,每次传入多个pid是没有意义的,如果是这样,为什么要让activityManager.getProcessMemoryInfo()只接收一个int数组呢?

#4


24  

Hackbod's is one of the best answers on Stack Overflow. It throws light on a very obscure subject. It helped me a lot.

Hackbod是关于栈溢出的最佳答案之一。它阐明了一个非常模糊的问题。这对我帮助很大。

Another really helpful resource is this must-see video: Google I/O 2011: Memory management for Android Apps

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


UPDATE:

更新:

Process Stats, a service to discover how your app manages memory explained at the blog post Process Stats: Understanding How Your App Uses RAM by Dianne Hackborn:

Process Stats,一个发现你的应用如何管理内存的服务,在博客文章Process Stats中解释:理解你的应用如何使用RAM, Dianne Hackborn:

#5


19  

Android Studio 0.8.10+ has introduced an incredibly useful tool called Memory Monitor.

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

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

What it's good for:

什么是好的:

  • Showing available and used memory in a graph, and garbage collection events over time.
  • 在图表中显示可用内存和使用的内存,以及随着时间的推移发生的垃圾收集事件。
  • Quickly testing whether app slowness might be related to excessive garbage collection events.
  • 快速测试应用程序的慢度是否与过度的垃圾收集事件有关。
  • Quickly testing whether app crashes may be related to running out of memory.
  • 快速测试应用程序崩溃是否与内存耗尽有关。

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

Figure 1. Forcing a GC (Garbage Collection) event on Android Memory Monitor

图1所示。在Android内存监视器上强制执行GC(垃圾收集)事件

You can have plenty good information on your app's RAM real-time consumption by using it.

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

#6


16  

1) I guess not, at least not from Java.
2)

1)我想没有,至少Java没有。

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

#7


2  

We found out that all the standard ways of getting the total memory of the current process have some issues.

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

  • Runtime.getRuntime().totalMemory(): returns JVM memory only
  • getruntime ().totalMemory():只返回JVM内存
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory() and anything else based on /proc/meminfo - returns memory info about all the processes combined (e.g. android_util_Process.cpp)
  • getmemoryinfo()、Process.getFreeMemory()以及任何基于/proc/meminfo的东西——返回所有进程的内存信息(例如android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - uses mallinfo() which return information about memory allocations performed by malloc() and related functions only (see android_os_Debug.cpp)
  • getnativeheapallocatedsize()——使用mallinfo(),它只返回关于malloc()执行的内存分配和相关函数的信息(参见android_os_Debug.cpp)
  • Debug.getMemoryInfo() - does the job but it's too slow. It takes about 200ms on Nexus 6 for a single call. The performance overhead makes this function useless for us as we call it regularly and every call is quite noticeable (see android_os_Debug.cpp)
  • getmemoryinfo()—完成这个任务,但是太慢了。Nexus 6的一次通话大约需要200毫秒。性能开销使得这个函数对我们来说毫无用处,因为我们经常调用它,并且每次调用都很明显(参见android_os_Debug.cpp)
  • ActivityManager.getProcessMemoryInfo(int[]) - calls Debug.getMemoryInfo() internally (see ActivityManagerService.java)
  • -在内部调用Debug.getMemoryInfo()

Finally, we ended up using the following code:

最后,我们使用了以下代码:

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;

It returns VmRSS metric. You can find more details about it here: one, two and three.

它返回VmRSS度量。你可以在这里找到更多的细节:1、2和3。


P.S. I noticed that the theme still has a lack of an actual and simple code snippet of how to estimate the private memory usage of the process if the performance isn't a critical requirement:

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;

#8


0  

There are a lot of answer above which will definitely help you but i (after 2 days of afford and research on adb memory tools) think i can help with my opinion too.

上面有很多答案肯定会对您有所帮助,但是我(在花了两天的时间研究adb内存工具之后)认为我也可以帮助您理解我的观点。

As Hackbod says : Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM. so there is no way you can get exact amount of memory per process.

正如Hackbod所言:因此,如果您将实际映射到每个进程的所有物理RAM,并将所有进程相加,您可能会得到一个比实际的总RAM大得多的数字。因此,您不可能获得每个进程的确切内存数量。

But you can get close to it by some logic..and I will tell how..

但是你可以用某种逻辑来接近它。我会告诉你…

There are some API like android.os.Debug.MemoryInfo and ActivityManager.getMemoryInfo() mentioned above which you already might have being read about and used but I will talk about another way (indirect one)

有一些API像android.os.Debug。上面提到的MemoryInfo和activitymanager。getmemoryinfo()你们可能已经读过并使用过了但我将用另一种方式(间接的)

So firstly you need to be a root user to get it work. Get into console with root privilege by executing su in process and get its output and input stream.Then pass id\n (enter) in ouputstream and write it to process output, If will get an inputstream containing uid=0, you are root user.

首先,你需要成为一个根用户来让它工作。通过在进程中执行su,获得控制台的根特权,并获得其输出和输入流。然后在ouputstream中传递id\n (enter),并将其写入进程输出,如果得到包含uid=0的inputstream,那么您就是根用户。

Now here is the logic which you will use in above process

这就是你将在上面的过程中用到的逻辑

When you get ouputstream of process pass you command (procrank, dumpsys meminfo etc...) with \n instead of id and get its inputstream and read, store the stream in bytes,char etc.. use raw data..and you are done!!!!!

当你获得进程的输出流通过你的命令(procranke, dumpsys meminfo等)时,用\n代替id,获取它的inputstream并读取,以字节、char等形式存储流。使用原始数据. .和你做! ! ! ! !

permission :

许可:

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

Check if you are root user :

检查您是否是根用户:

try {
    // su command to get root access
    Process 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 enter
        dataOutputStream.writeBytes("id\n");                   
        dataOutputStream.flush();
        String Uid = dataInputStream.readLine();
        // read output and check if uid is there
        if (Uid.contains("uid=0")) {                           
             // yes sir you are root user
         } 
    }
} catch (Exception e) {
}

Execute your command with su

使用su执行命令

try {
      // su
      Process process = Runtime.getRuntime().exec("su");         
      DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream());
      if (dataOutputStream != null) {
          // adb command
          dataOutputStream.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 stream 
          try {
              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();
      }
} catch (Exception e) {
}

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

logcat:

This is not a solution as you wont get data in some instance or from any API, instead you get a raw data in a single string just as you get from console, which is very hard to manage and complex to store as you will need to separate it manually.

这不是一个解决方案,你不会得到一些实例中的数据或从任何API,而不是得到一个字符串的原始数据就像你从控制台,这是非常难以管理和复杂的存储需要单独的手动。

This is just a try, please correct me if I am wrong

这只是一个尝试,如果我错了请纠正我