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


Answers:


453

有两种方式可以考虑您的短语“可用的应用程序堆大小”:

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

  2. 考虑到Android OS版本和用户设备硬件的限制,我的应用使用多少堆?

有不同的方法来确定上述各项。

对于上述项目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));

此方法告诉您如果您的应用程序适当尊重当前设备的限制,以及其他应用程序在不被多次粗暴地强制进入/ 循环的情况下运行的权利,则大约使用您的应用程序多少兆字节当您的大象应用在Android按摩浴缸中洗澡时,内存被冲出了内存。onStop()onResume()

据我所知,这种区别尚无明确记载,但我已经在五种不同的Android设备(见下文)上验证了这一假设,并令我满意的是,这是正确的解释。

对于Android的普通版本,maxMemory()通常返回的兆字节数与指示的兆字节数相同getMemoryClass()(即,后者值的大约一百万倍)。

这两种方法可以发生分歧的唯一情况(据我所知)是在运行Android版本(例如CyanogenMod)的有根设备上进行的,该设备允许用户手动选择每个应用程序应允许的堆大小。例如,在CM中,此选项显示在“ CyanogenMod设置” /“性能” /“ VM堆大小”下。

注意:请注意,手动设置此值可以改善您的系统,特别是如果您选择的值小于设备的正常值。

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

  • G1:
    • 将VM堆大小设置为16MB:
      • maxMemory:16777216
      • getMemoryClass:16
    • 将VM堆大小设置为24MB:
      • maxMemory:25165824
      • getMemoryClass:16
  • Moto Droid:
    • 将VM堆大小设置为24MB:
      • maxMemory:25165824
      • getMemoryClass:24
    • 将VM堆大小设置为16MB:
      • maxMemory:16777216
      • getMemoryClass:24
  • 连结一:
    • 将VM Heap大小设置为32MB:
      • maxMemory:33554432
      • getMemoryClass:32
    • 将VM Heap大小设置为24MB:
      • maxMemory:25165824
      • getMemoryClass:32
  • 优派GTab:
    • 将VM堆大小设置为32:
      • maxMemory:33554432
      • getMemoryClass:32
    • 将VM堆大小设置为64:
      • maxMemory:67108864
      • getMemoryClass:32

除上述内容外,我还在运行Ice Cream Sandwich的Novo7 Paladin平板电脑上进行了测试。这实际上是ICS的普通版本,只是我通过一个简单的过程使平板电脑扎根,而不是替换整个OS,特别是它不提供允许手动调整堆大小的界面。

对于该设备,结果如下:

  • Novo7
    • maxMemory:62914560
    • getMemoryClass:60

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

  • HTC One X
    • maxMemory:67108864
    • getMemoryClass:64

并且(根据akauppi的评论):

  • 三星Galaxy Core Plus
    • maxMemory :(未在注释中指定)
    • getMemoryClass:48
    • largeMemoryClass:128

根据cmcromance的评论:

  • 银河S3(果冻豆)大堆
    • maxMemory:268435456
    • getMemoryClass:64

并且(按腾讯的评论):

  • LG Nexus 5(4.4.3)正常
    • maxMemory:201326592
    • getMemoryClass:192
  • LG Nexus 5(4.4.3)大堆
    • maxMemory:536870912
    • getMemoryClass:192
  • Galaxy Nexus(4.3)正常
    • maxMemory:100663296
    • getMemoryClass:96
  • Galaxy Nexus(4.3)大堆
    • maxMemory:268435456
    • getMemoryClass:96
  • Galaxy S4 Play商店版(4.4.2)正常
    • maxMemory:201326592
    • getMemoryClass:192
  • Galaxy S4 Play商店版(4.4.2)大堆
    • maxMemory:536870912
    • getMemoryClass:192

其他设备

  • 华为Nexus 6P(6.0.1)正常
    • maxMemory:201326592
    • getMemoryClass:192

我没有使用自Honeycomb以来可用的特殊android:largeHeap =“ true”清单选项来测试这两种方法,但是由于cmcromance和腾讯,我们确实有一些示例largeHeap值,如上所述。

我的预期(这似乎是由largeHeap数以上支持)将是这个选项将有类似于通过扎根OS手动设置堆的效果-即,它会提高的价值maxMemory(),同时保留getMemoryClass()。还有另一种方法getLargeMemoryClass(),该方法指示使用largeHeap设置的应用程序允许多少内存。getLargeMemoryClass()的文档指出:“大多数应用程序都不需要此内存量,而应保持在getMemoryClass()限制之内。”

如果我猜对了,那么使用该选项与使用已通过根操作系统升级堆的用户提供的空间(即,如果您的应用使用了额外的内存,它可能无法与用户同时运行的任何其他应用程序配合使用)。

请注意,内存类显然不必为8MB的倍数。

从上面我们可以看到,getMemoryClass()对于给定的设备/ OS配置,结果是不变的,而当用户对堆的设置不同时,maxMemory()值也会更改。

我自己的实际经验是,在G1(内存级别为16)上,如果我手动选择24MB作为堆大小,即使允许将内存使用量提高到20MB,我也可以正常运行。最高可达24MB,尽管我还没有尝试过)。但是由于我自己的应用程序的笨拙性,其他类似的大型应用程序可能会从内存中清除。相反,如果这些其他高维护性应用程序被用户带到前台,则我的应用程序可能会从内存中清除。

因此,您不能超过所指定的内存量maxMemory()。并且,您应尝试保持在所指定的范围内getMemoryClass()。如果其他所有方法都失败了,那么这样做的一种方法可能是以节省内存的方式来限制此类设备的功能。

最后,如果您确实打算超过中指定的兆字节数getMemoryClass(),那么我的建议是在保存和恢复应用程序状态方面进行长期而艰苦的工作,以便在onStop()/ onResume()周期发生时用户的体验几乎不会中断。

就性能而言,我将我的应用程序限制为运行2.2及更高版本的设备,这意味着几乎所有运行我的应用程序的设备的memoryClass都将为24或更高。因此,我可以设计为最多占用20MB的堆,并确信我的应用程序可以与用户同时运行的其他应用程序很好地协同工作。

但是总会有一些扎根用户将2.2或更高版本的Android加载到较旧的设备(例如G1)上。理想情况下,遇到这样的配置时,即使maxMemory()告诉您可以比getMemoryClass()告诉您应该定位的16MB高得多,也应该减少内存使用。并且,如果您不能可靠地确保您的应用程序能够在该预算范围内运行,则至少要确保onStop()/ onResume()无缝运行。

getMemoryClass()如上面的Diane Hackborn(hackbod)所指出的,仅可用于API级别5(Android 2.0),因此,正如她所建议的那样,您可以假定运行早期版本操作系统的任何设备的物理硬件都是经过设计的以最佳方式支持占用不超过16MB堆空间的应用程序。

相比之下,maxMemory()根据文档,从API级别1开始一直可用 maxMemory(),在2.0之前的版本上,可能会返回16MB的值,但是我确实在(后来的)CyanogenMod版本中看到该用户可以选择低至12MB的堆值,这大概会导致较低的堆限制,因此我建议您继续测试该maxMemory()值,即使对于2.0之前的OS版本也是如此。如果需要将该值设置为甚至小于16MB,甚至可能需要拒绝运行,甚至可能无法运行maxMemory()


2
@Carl您好,Carl,谢谢您的精彩帖子。我在带有JellyBean的Galaxy S3上测试了android:largeHeap =“ true”选项。这是结果。[maxMemory:256.0MB,memoryClass:64MB](不带
largeheap

1
@Carl Haha真是荣幸!:)
cmcromance

1
对于HTC One X:maxMemory:67108864 memoryClass:64
Kishore

1
LG Nexus 5(4.4.3)(常规):maxMemory:201326592,memoryClass:192 | LG Nexus 5(4.4.3)(大堆):maxMemory:536870912,memoryClass:192
ian.shaun.thomas 2014年

1
非常精美的文档。请将其提供给android。我找不到合适的Android文档
Jimit Patel

20

官方API是:

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


13

Debug.getNativeHeapSize()我会认为,会成功的。但是从1.0开始就存在。

Debug班有许多用于跟踪分配和其他性能方面的问题很大的方法。另外,如果您需要检测内存不足的情况,请签出Activity.onLowMemory()


谢谢尼尔。当应用程序在关闭调试的情况下运行时,这是否有效?
hpique 2010年

2
我对此很陌生,但是我不认为本机堆与该问题所引用的应用程序(Dalvik)堆相同。本机堆为由本机代码分配的位图数据提供支持,而应用程序堆则保存Java应用程序数据。我很想知道,本机堆是根据应用程序堆的限制计算的,并且在3.0之后,分配实际上发生在应用程序堆上。黛安·哈克伯恩(hackbod)在这里发布了有关此主题的文章: stackoverflow.com/questions/1945142/bitmaps-in-android 但即使对于3.0,应用程序堆上也有非“本机”数据。
卡尔

1
由于最近进行了一些Android更新(可能是KitKat 4.4),因此位图位于JVM堆中。
akauppi 2014年

13

这是您的操作方式:

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

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

获取您的应用程序当前使用的堆空间:

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

获取您的应用程序现在可以使用多少堆(可用内存):

long availableMemory=maxMemory-usedMemory;

并且,为了很好地格式化它们,可以使用:

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

5

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

Runtime.getRuntime().maxMemory()

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


由于doc for getMemoryClass似乎暗示该数字可能与您的vm可用的堆大小不同,并且由于doc for getNativeHeapSize...沉默寡言,我真的认为这Runtime.getRuntime().maxMemory()是最佳答案。
BoD

3

有些操作比Java堆空间管理器快。将操作延迟一段时间会释放存储空间。您可以使用此方法来避免堆大小错误:

waitForGarbageCollector(new Runnable() {
  @Override
  public void run() {

    // Your operations.
  }
});

/**
 * Measure used memory and give garbage collector time to free up some
 * 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 availavle, go to next operation:

    callback.run();
  }
}

测试好。您能否详细说明这两件事:availableMemoryPercentage和MIN_AVAILABLE_MEMORY_PERCENTAGE?一些解释?
Noor Hossain

1
AvailableMemoryPercentage根据公式:当前有多少设备内存可用。MIN_AVAILABLE_MEMORY_PERCENTAGE是您的自定义参数,是您开始等待垃圾收集器执行其工作的阈值。
Zon

1

华硕Nexus 7(2013)32Gig:getMemoryClass()= 192 maxMemory()= 201326592

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

当我确定memoryclass低时,我将需要重组项目以加载更少的资源。
Java中有没有办法查看当前堆大小?(我在调试时可以在logCat中清楚地看到它,但是我希望有一种方法可以在代码中看到它以适应,例如currentheap>(maxmemory / 2)是否卸载高质量位图而加载低质量


上所述Nexus7-2013,getLargeMemoryClass()= 512
akauppi

0

您是用编程的方式还是在开发和调试时?如果是后者,则可以在Eclipse中从DDMS角度看到该信息。当您的仿真器(甚至可能是插入的物理电话)正在运行时,它将在左侧窗口中列出活动进程。您可以选择它,并且有一个选项可以跟踪堆分配。


我的意思是以编程方式。澄清了问题。谢谢。
hpique 2010年

1
我建议你看看这个帖子由Android平台的程序员,然后是一个:stackoverflow.com/questions/2298208/...
史蒂夫海利

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

值是b

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

值是MB


什么类型的rt?声明在哪里?
FindOut_Quran 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.