在 Android 中检测应用程序堆大小

如何通过编程检测 Android 应用程序可用的应用程序堆大小?

我听说在 SDK 的后期版本中有一个函数可以实现这个功能。无论如何,我正在寻找的解决方案,工程为1.5和以上。

110864 次浏览

您是指以编程方式,还是仅仅在开发和调试时?如果是后者,您可以从 Eclipse 中的 DDMS 透视图中看到这些信息。当您的模拟器(甚至可能是已插入的物理电话)运行时,它将在左侧的窗口中列出活动进程。您可以选择它,并且有一个跟踪堆分配的选项。

我觉得 Debug.getNativeHeapSize()应该能行,不过它从1.0版就开始运行了。

Debug类有许多很好的方法来跟踪分配和其他性能问题。另外,如果您需要检测内存不足的情况,请查看 Activity.onLowMemory()

官方的 空气污染指数是:

这是在2.0中引入的,出现了更大的内存设备。您可以假设运行先前版本操作系统的设备正在使用原始内存类(16)。

这将返回以字节为单位的最大堆大小:

Runtime.getRuntime().maxMemory()

我使用的是 ActivityManager.getMemory yClass () ,但是在 CyanogenMod 7(我没有在其他地方测试它)上,如果用户手动设置堆大小,它会返回错误的值。

有两种方法可以理解您的短语“可用的应用程序堆大小”:

  1. 在触发硬性错误之前,我的应用程序可以使用多少堆

  2. 考虑到 Android 操作系统版本和用户设备硬件的限制,我的应用程序使用了多少堆 应该

有一个不同的方法来确定每一个以上。

以上第1项: maxMemory()

它可以按如下方式调用(例如,在主活动的 onCreate()方法中) :

Runtime rt = Runtime.getRuntime();
long maxMemory = rt.maxMemory();
Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));

这个方法告诉你你的应用程序要使用的总 字节是多少。

以上第2项: getMemoryClass()

可援引如下:

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();
Log.v("onCreate", "memoryClass:" + Integer.toString(memoryClass));

这个方法可以告诉你你的应用程序 应该大约使用了多少 兆字节的堆,如果它想正确地尊重当前设备的限制,以及其他应用程序的运行权利,而不是被重复地强制进入 onStop()/onResume()循环,因为它们被粗暴地冲出内存,而你的大象应用程序在安卓按摩浴缸里洗澡。

据我所知,这个区别并没有被清楚地记录下来,但是我已经在五个不同的 Android 设备上测试了这个假设(见下文) ,并且证实了我自己的满意,这是一个正确的解释。

对于一个普通的 Android 版本,maxMemory()通常返回与 getMemoryClass()相同的兆字节数(也就是说,大约是后一个值的100万倍)。

这两种方法唯一可能出现分歧的情况是在一个运行 Android 版本的根设备上,比如 CyanogenMod,它允许用户手动为每个应用设置 选择堆大小。例如,在 CM 中,此选项出现在“ CyanogenMod 设置”/“ Performance”/“ VM 堆大小”下。

注意: 手动设置这个值可能会扰乱你的系统,特别是如果你选择了一个比你的设备正常值更小的值。

下面是我的测试结果,显示了运行 CyanogenMod 的四个不同设备的 maxMemory()getMemoryClass()返回的值,每个设备使用两个不同的(手动设置的)堆值:

  • 答案1:
    • 将 VM 堆大小设置为16MB:
      • MaxMemory: 16777216
      • GetMemory 类: 16
    • 将 VM 堆大小设置为24MB:
      • MaxMemory: 25165824
      • GetMemory 类: 16
  • 摩托罗拉机器人:
    • 将 VM 堆大小设置为24MB:
      • MaxMemory: 25165824
      • GetMemory 类: 24
    • 将 VM 堆大小设置为16MB:
      • MaxMemory: 16777216
      • GetMemory 类: 24
  • Nexus One:
    • 将 VM 堆大小设置为32 MB:
      • MaxMemory: 33554432
      • GetMemory 类: 32
    • 将 VM 堆大小设置为24 MB:
      • MaxMemory: 25165824
      • GetMemory 类: 32
  • 优派 GTab:
    • 将 VM 堆大小设置为32:
      • MaxMemory: 33554432
      • GetMemory 类: 32
    • 将 VM 堆大小设置为64:
      • MaxMemory: 67108864
      • GetMemory 类: 32

除此之外,我还在 Novo7圣骑士平板电脑上测试了冰淇淋三明治。 这实际上是 ICS 的一个原始版本,除了我通过一个简单的过程植根于平板电脑,这个过程不能替代整个操作系统,特别是不能提供一个允许手动调整堆大小的界面。

对于这种设备,结果如下:

  • Novo7
    • MaxMemory: 62914560
    • GetMemory 类: 60

另外(根据 Kishore 在下面的评论) :

  • HTC One X
    • MaxMemory: 67108864
    • GetMemory 类: 64

而且(按照考皮的评论) :

  • 三星 Galaxy Core Plus
    • MaxMemory: (未在注释中指定)
    • GetMemory 类别: 48
    • 大型记忆类别: 128

根据一条来自 cmcromance 的评论:

  • Galaxy S3(果冻豆)大堆
    • MaxMemory: 268435456
    • GetMemory 类: 64

而且(根据腾讯的评论) :

  • LG Nexus5(4.4.3)正常
    • MaxMemory: 201326592
    • GetMemory 类: 192
  • LG Nexus 5(4.4.3)大型堆
    • MaxMemory: 536870912
    • GetMemory 类: 192
  • Galaxy Nexus (4.3)正常
    • MaxMemory: 100663296
    • GetMemory 类: 96
  • Galaxy Nexus (4.3)大堆
    • MaxMemory: 268435456
    • GetMemory 类: 96
  • Galaxy S4游戏储存版(4.4.2)正常
    • MaxMemory: 201326592
    • GetMemory 类: 192
  • Galaxy S4 Play Store Edition (4.4.2)大型堆
    • MaxMemory: 536870912
    • GetMemory 类: 192

其他器件

  • 华为 nexus6p (6.0.1)正常
    • MaxMemory: 201326592
    • GetMemory 类: 192

我还没有使用特殊的 android 系统来测试这两种方法: largeHeap = 从 Honeycomb 开始就可以使用的“ true”清单选项,但是多亏了 cmcromance 和腾讯,我们确实有了一些大堆值样本,如上所述。

我的 期望(它似乎受到上面大堆数字的支持)是,这个选项的效果类似于通过根操作系统手动设置堆——也就是说,它会提高 maxMemory()的值,同时不使用 getMemoryClass()。还有另一个方法 getLargeMemoryClass () ,它指示使用 largeHeap 设置的应用程序允许的内存量。GetLargeMemoryClass ()的文档指出,“大多数应用程序不应该需要这么大的内存量,而应该保持 getMemoryClass ()的限制。”

如果我猜对了,那么使用这个选项的好处(和风险)将与使用通过根操作系统升级堆的用户提供的空间(即,如果你的应用程序使用额外的内存,它可能无法很好地与用户同时运行的其他应用程序一起运行)相同。

注意,内存类显然不需要是8MB 的倍数。

从上面我们可以看到,对于给定的设备/OS 配置,getMemoryClass()结果是不变的,而 maxMemory ()值在用户对堆进行不同的设置时会发生变化。

我自己的实际经验是,在 G1(它有16个内存类)上,如果我手动选择24 MB 作为堆大小,即使我的内存使用量被允许上升到20 MB,我也可以正常运行(可能高达24 MB,尽管我还没有尝试过)。但是其他类似的大型应用程序可能会因为我自己的应用程序过于庞大而从内存中删除。相反,如果用户将这些高维护性的应用程序放在前台,那么 天啊应用程序可能会从内存中被刷新。

因此,您不能遍历 maxMemory()指定的内存量。而且,你应该 试试看停留在指定的限制由 getMemoryClass()。如果其他方法都失败了,一种方法可能是以节省内存的方式限制这些设备的功能。

最后,如果你确实打算超过 getMemoryClass()中指定的兆字节数,我的建议是长期努力地保存和恢复应用程序的状态,这样用户的体验几乎不会受到 onStop()/onResume()循环的干扰。

就我而言,出于性能考虑,我将我的应用程序限制在运行2.2及以上版本的设备上,这意味着几乎所有运行我的应用程序的设备都将具有24或更高的内存级别。因此,我可以设计占用高达20MB 的堆,并感到非常有信心,我的应用程序将发挥良好的其他应用程序的用户可能在同一时间运行。

但是总会有一些固定用户把2.2或更高版本的 Android 加载到旧设备上(比如 G1)。当您遇到这样的配置时,理想情况下,您应该减少内存使用,即使 maxMemory()告诉您可以比 getMemoryClass()告诉您的 应该所针对的16MB 大得多。如果你不能可靠地确保你的应用程序将生活在这个预算,那么至少要确保 onStop()/onResume()工作无缝。

正如 Diane Hackborn (hackbod)指出的那样,getMemoryClass()只能回到 API 级别5(Android 2.0) ,因此,正如她建议的那样,你可以假设任何运行早期版本操作系统的设备的物理硬件的设计都是为了最佳地支持应用程序占用不超过16MB 的堆空间。

相比之下,根据文档,maxMemory()可以一直追溯到 API 级别1。在2.0之前的版本中,maxMemory()可能会返回16MB 的值,但是在我的(更晚的) CyanogenMod 版本中,用户可以选择低至12MB 的堆值,这可能会导致更低的堆限制,所以我建议你继续测试 maxMemory()值,即使是2.0之前的操作系统版本。如果您需要超过 maxMemory()所允许的值,那么您甚至可能不得不拒绝运行这个值甚至低于16MB 的不太可能的事件。

Runtime rt = Runtime.getRuntime();
rt.maxMemory()

值是 b

ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()

值为 MB

华硕 Nexus 7(2013)32Gig: GetMemory 类() = 192 MaxMemory () = 201326592

我犯了一个错误,在 Nexus 7上开发了我的游戏原型,然后发现它几乎立刻在我妻子的4.04平板电脑上耗尽了内存(内存类别48,最大内存50331648)

当我确定内存类比较低时,我需要重新组织我的项目,以便加载更少的资源。
Java 中有没有查看当前堆大小的方法?(在调试时,我可以在 logCat 中清楚地看到它,但是我希望能够在代码中看到它的适应性,例如,如果 currentheap > (maxmemory/2)卸载高质量的位图加载低质量的位图

你要这么做:

获取应用程序可以使用的最大堆大小:

Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();

了解你的应用程序当前使用了多少堆:

long usedMemory=runtime.totalMemory() - runtime.freeMemory();

了解你的应用程序现在能使用多少堆(可用内存) :

long availableMemory=maxMemory-usedMemory;

为了更好地格式化它们,您可以使用:

String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize);

有些操作比 Java 堆空间管理器更快。延迟行动可以释放一段时间的内存空间。您可以使用此方法来转义堆大小错误:

waitForGarbageCollector(new Runnable() {
@Override
public void run() {
// Your operations.
}
});


/**
* Measure used memory and give garbage collector time to free up some
* of the space.
*
* @param callback Callback operations to be done when memory is free.
*/
public static void waitForGarbageCollector(final Runnable callback) {


Runtime runtime;
long maxMemory;
long usedMemory;
double availableMemoryPercentage = 1.0;
final double MIN_AVAILABLE_MEMORY_PERCENTAGE = 0.1;
final int DELAY_TIME = 5 * 1000;


runtime =
Runtime.getRuntime();


maxMemory =
runtime.maxMemory();


usedMemory =
runtime.totalMemory() -
runtime.freeMemory();


availableMemoryPercentage =
1 -
(double) usedMemory /
maxMemory;


if (availableMemoryPercentage < MIN_AVAILABLE_MEMORY_PERCENTAGE) {
try {
Thread.sleep(DELAY_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
waitForGarbageCollector(
callback);
} else {
// Memory resources are available, go to next operation:
callback.run();
}
}