如何在安卓系统中获取当前内存使用情况?

135

我使用了/proc/meminfo并解析了命令响应,但结果显示:

MemTotal:94348 kB MemFree:5784 kB

这意味着只有5MB的空闲内存。在Android手机上是否可能出现这种情况? 我的手机上只安装了5-6个应用程序,并且没有其他任务正在运行。但是这个命令仍然显示非常少的可用内存。

有人能澄清一下吗?或者是否有其他获取Android内存使用情况的方法?


2
你是想查看每个设备或每个应用程序的可用内存吗?如果是每个应用程序,那么需要在堆上计算,类似于 Debug.getNativeHeapFreeSize() - IgorGanapolsky
1
使用/proc/meminfo计算空闲内存(RAM),您必须获取MemFreeBuffersCachedSwapCached的总和。 Android提供了一个用于此目的的API,可在API 16及以上版本上运行。如果您的目标是旧的API,则Meminfo非常有用。 - A.B.
12个回答

190

注意:本答案测量的是设备的内存使用/可用情况,而不是您的应用程序可用情况。要测量您的应用程序正在执行的操作以及允许执行的操作,请使用 Android 开发者的回答


Android 文档 - ActivityManager.MemoryInfo

  1. 解析 /proc/meminfo 命令。您可以在这里找到参考代码:获取 Android 中的内存使用情况

  2. 使用下面的代码获取当前 RAM:

  3. MemoryInfo mi = new MemoryInfo();
    ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
    activityManager.getMemoryInfo(mi);
    double availableMegs = mi.availMem / 0x100000L;
    
    //Percentage can be calculated for API 16+
    double percentAvail = mi.availMem / (double)mi.totalMem * 100.0;
    

    0x100000L数值的解释

    1024 bytes      == 1 Kibibyte 
    1024 Kibibyte   == 1 Mebibyte
    
    1024 * 1024     == 1048576
    1048576         == 0x100000
    

    很明显,这个数字用于将字节转换成Mebibyte。

    P.S:我们只需要计算总内存一次。因此,在您的代码中仅调用点1一次,然后可以重复调用点2的代码。


我想要检查内存大小。什么是MemoryInfo? - Piraba
什么是avilMem?我们如何获取它? - Vivek Warde
您可以使用MemoryInfo类来获取availMem。在此处查看:http://javadox.com/com.google.android/android/4.1.1.4/android/app/ActivityManager.MemoryInfo.html#availMem - Badal
2
转换为双精度浮点数,否则percentAvail将为0。 - blueether
2
@Rolfツ 抱歉,1024字节等于1 Kibibyte,1024 Kibibyte等于1 MibiByte。Kilo和Mega是十进制前缀。1000字节=1千字节。这在答案中也被错误解释了。 - JacksOnF1re
显示剩余4条评论

126

这取决于您希望获取的内存查询的定义。


通常,您需要了解堆内存的状态,因为如果它使用过多的内存,您将收到OOM并使应用程序崩溃。

为此,您可以检查以下值:

final Runtime runtime = Runtime.getRuntime();
final long usedMemInMB=(runtime.totalMemory() - runtime.freeMemory()) / 1048576L;
final long maxHeapSizeInMB=runtime.maxMemory() / 1048576L;
final long availHeapSizeInMB = maxHeapSizeInMB - usedMemInMB;

当"usedMemInMB"变量接近"maxHeapSizeInMB"时,availHeapSizeInMB越接近于零,离OOM就越近。(由于内存碎片化,可能在达到零之前就会发生OOM。)

这也是内存使用的DDMS工具显示的内容。


另外,还有实际的RAM使用情况,即整个系统使用了多少RAM-查看被接受的答案以计算它。


更新:自Android O开始,您的应用程序也会使用本地RAM(至少用于位图存储,这通常是巨大的内存使用的主要原因),而不仅仅是堆,因此情况已经改变,并且您会获得更少的OOM(因为堆不再包含位图,请参见此处),但如果您怀疑存在内存泄漏,仍然应该注意内存使用情况。在Android O上,如果您有可能导致旧版本OOM的内存泄漏,似乎它将会在没有您能够捕捉到的情况下崩溃。以下是检查内存使用情况的方法:

val nativeHeapSize = Debug.getNativeHeapSize()
val nativeHeapFreeSize = Debug.getNativeHeapFreeSize()
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize

但我认为最好使用IDE的性能分析工具,它以图形方式实时显示数据。

因此,在Android O上的好消息是,由于存储过多的大型位图而导致崩溃变得更加困难,但坏消息是我不认为在运行时捕获这种情况是可能的。


编辑:似乎Debug.getNativeHeapSize()会随时间改变,因为它向您显示应用程序的总最大内存。因此,这些函数仅用于性能分析工具,以显示您的应用程序使用了多少内存。

如果您想获取真正的总本地RAM和可用RAM,请使用以下方法:

val memoryInfo = ActivityManager.MemoryInfo()
(getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).getMemoryInfo(memoryInfo)
val nativeHeapSize = memoryInfo.totalMem
val nativeHeapFreeSize = memoryInfo.availMem
val usedMemInBytes = nativeHeapSize - nativeHeapFreeSize
val usedMemInPercentage = usedMemInBytes * 100 / nativeHeapSize
Log.d("AppLog", "total:${Formatter.formatFileSize(this, nativeHeapSize)} " +
        "free:${Formatter.formatFileSize(this, nativeHeapFreeSize)} " +
        "used:${Formatter.formatFileSize(this, usedMemInBytes)} ($usedMemInPercentage%)")

哇。如此简单,却如此真实! - Ran
1
@batmaci 堆内存只是应用程序总内存使用的一部分。还有本地内存使用,通常用于网页、游戏和一些重型用途。通常应用程序只需要查看堆内存,因为与设备RAM相比,它相当低,并且如果达到了堆内存上限,应用程序将崩溃(即使有大量可用的RAM)。 - android developer
这是一个完美的代码片段,非常有用于检查OOM,非常感谢。 - aolphn
@AlphaOF 谢谢你,但是在安卓 O 上情况已经发生了变化。我已经更新了答案以适应那里的情况。 - android developer
顺便问一下,从代码中获取的内存信息与内存分析器获取的不同,你知道为什么吗? - aolphn
显示剩余12条评论

32

以下是计算当前运行应用程序内存使用的方法:

public static long getUsedMemorySize() {

    long freeSize = 0L;
    long totalSize = 0L;
    long usedSize = -1L;
    try {
        Runtime info = Runtime.getRuntime();
        freeSize = info.freeMemory();
        totalSize = info.totalMemory();
        usedSize = totalSize - freeSize;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return usedSize;

}

6
这是一种简单的方法,但是在文档中已经指出,Runtime类的freeMemory()方法返回当前程序或应用可用的内存。因此,在使用时要注意这一点。 - Aksel Fatih
3
@Peter - 是的,它回答了与所问不同的问题,因此在这方面是“错误”的。另一方面,对于应用程序开发人员通常需要知道的内容来说,这是“正确”的:设备上的内存总状态很少有意义 - 如果用户运行了大量的应用程序,则操作系统应该利用其大部分内存 - 否则会低效。操作系统需要了解所接受的答案以知道何时开始终止最近未使用的应用程序。但应用程序员需要知道此答案(以及类似的Android开发人员的答案)告诉您什么,与runtime.maxMemory相比。 - ToolmakerSteve
这个解决方案仅适用于Runtime向应用程序公开的内存。这并不能提供OP所需的整个系统内存的洞察力。 - A.B.
2
在许多设备(例如小米)上,Runtime.freeMemory() 返回 0,而 Runtime.totalMemory() 只返回当前分配的内存。 - artem

18

另一种方式(目前在我的G1上显示有25MB可用空间):

MemoryInfo mi = new MemoryInfo();
ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
activityManager.getMemoryInfo(mi);
long availableMegs = mi.availMem / 1048576L;

嗨,Alex, 非常感谢你的帮助!还有一个问题。这段代码给我可用的RAM。我也想显示总RAM。如何获取? - Badal
1
@Badal 我不知道有哪个Java API可以做到那个。最好还是解析/proc/meminfo文件。 - yanchenko

14

Linux的内存管理哲学是“空闲内存就是浪费内存”。

我认为接下来的两行将显示“缓冲区”中有多少内存和“高速缓存”中有多少内存。虽然它们之间有所不同(请不要问这个差异是什么 :) ),但它们都大致相当于用于缓存文件数据和元数据的内存量。

在Linux系统上,一个更有用的免费内存指南是使用free(1)命令;在我的桌面上,它报告的信息如下:

$ free -m
             总共       已用       空闲     共享的    缓冲区     高速缓存
内存:         5980       1055       4924          0         91        374
-/+ 缓存/空闲:        589       5391
交换:        6347          0       6347

“+/- 缓存/空闲”行是神奇的一行,它报告说我实际上有约589兆字节的活动所需进程内存和约5391兆字节的“空闲”内存,因为如果内存可能被更有利地用于其他用途,那么91+374兆字节的缓冲区/缓存内存可以被丢弃。

(我的机器已经开机3个小时了,几乎什么都没做,只是在stackoverflow上浏览,这就是为什么我有那么多空闲内存。)

如果Android没有安装free(1),您可以使用/proc/meminfo文件自己计算;我只是喜欢free(1)输出格式。 :)


1
@Igor,那么你应该使用cat /proc/meminfo。它提供了更详细的信息,但是MemFreeBuffersCached这几行可能是最重要的。 - sarnold

7

我参考了一些文章。

参考资料:

这个getMemorySize()方法返回了具有总内存大小和可用内存大小的MemorySize。
我不完全相信这段代码。
这段代码是在LG G3 cat.6 (v5.0.1)上测试的。

    private MemorySize getMemorySize() {
        final Pattern PATTERN = Pattern.compile("([a-zA-Z]+):\\s*(\\d+)");

        MemorySize result = new MemorySize();
        String line;
        try {
            RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
            while ((line = reader.readLine()) != null) {
                Matcher m = PATTERN.matcher(line);
                if (m.find()) {
                    String name = m.group(1);
                    String size = m.group(2);

                    if (name.equalsIgnoreCase("MemTotal")) {
                        result.total = Long.parseLong(size);
                    } else if (name.equalsIgnoreCase("MemFree") || name.equalsIgnoreCase("Buffers") ||
                            name.equalsIgnoreCase("Cached") || name.equalsIgnoreCase("SwapFree")) {
                        result.free += Long.parseLong(size);
                    }
                }
            }
            reader.close();

            result.total *= 1024;
            result.free *= 1024;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    private static class MemorySize {
        public long total = 0;
        public long free = 0;
    }

我知道 Pattern.compile() 很耗费资源,因此您可以将其代码移动到类成员中。


4
我看了一下Android源代码树。 在com.android.server.am.ActivityManagerService.java中(这是由android.app.ActivityManager公开的内部服务)。
public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
    final long homeAppMem = mProcessList.getMemLevel(ProcessList.HOME_APP_ADJ);
    final long hiddenAppMem = mProcessList.getMemLevel(ProcessList.HIDDEN_APP_MIN_ADJ);
    outInfo.availMem = Process.getFreeMemory();
    outInfo.totalMem = Process.getTotalMemory();
    outInfo.threshold = homeAppMem;
    outInfo.lowMemory = outInfo.availMem < (homeAppMem + ((hiddenAppMem-homeAppMem)/2));
    outInfo.hiddenAppThreshold = hiddenAppMem;
    outInfo.secondaryServerThreshold = mProcessList.getMemLevel(
            ProcessList.SERVICE_ADJ);
    outInfo.visibleAppThreshold = mProcessList.getMemLevel(
            ProcessList.VISIBLE_APP_ADJ);
    outInfo.foregroundAppThreshold = mProcessList.getMemLevel(
            ProcessList.FOREGROUND_APP_ADJ);
}

在android.os.Process.java文件中:

/** @hide */
public static final native long getFreeMemory();

/** @hide */
public static final native long getTotalMemory();

它从android_util_Process.cpp调用JNI方法。

结论

MemoryInfo.availMem = /proc/meminfo中的MemFree + Cached。

注释

总内存在API级别16中添加。


2
/** accurate value under limitation */
int getAccurateAppUsedMemory() {
    ActivityManager manager = context.getSystemService(Context.ACTIVITY_SERVICE);
    Debug.MemoryInfo info = manager.getProcessMemoryInfo(new int[]{Process.myPid()})[0];
    // Warning: there's a limitation of invocation frequency on Android Q or later
    return info.getTotalPss(); // in kB
}

/** approximate value without limitation */
int getApproximateAppUsedMemory() {
    Debug.MemoryInfo info = new Debug.MemoryInfo();
    Debug.getMemoryInfo(info);
    return info.getTotalPss(); // in kB
}

1

您还可以使用Android SDK自带的DDMS工具。它可以帮助获取Java代码和本地C / C ++代码的内存分配情况。


0
public static boolean isAppInLowMemory(Context context) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
    activityManager.getMemoryInfo(memoryInfo);

    return memoryInfo.lowMemory;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接