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

如何以编程方式检测Android应用程序可用的应用程序堆大小?

我听说有一个函数可以在SDK的更高版本中执行此操作。 无论如何,我正在寻找适用于1.5及以上的解决scheme。

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

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

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

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

对于上面的第1项: maxMemory()

可以调用它(例如,在您的主要活动的onCreate()方法中),如下所示:

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

这个方法告诉你允许你的应用程序使用多less堆字节

对于上面的第2项: getMemoryClass()

可以如下调用它:

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

这个方法告诉你应用程序应该使用多less兆字节的堆,如果它想要正确地尊重当前设备的限制,并且其他应用程序不需要反复强制进入onStop() / onResume()循环,因为他们粗暴地冲出内存,而你的elephantine应用程序在安卓按摩浴缸洗澡。

就我所知,这个区别并没有明确的logging,但是我已经在五个不同的Android设备上testing了这个假设(见下面),并且已经确认我自己的满意度,这是一个正确的解释。

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

这两种方法可以分开的唯一情况是(运行Android版本的根设备,例如CyanogenMod),它允许用户手动select应该为每个应用程序允许多大的堆大小。 例如,在CM中,此选项出现在“CyanogenMod设置”/“性能”/“VM堆大小”下。

注意:注意设置这个值可以大大提高您的系统的性能,特别是如果您为设备select比正常值更小的值。

这里是我的testing结果,显示了运行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
  • Nexus One:
    • 将VM Heap大小设置为32 MB时:
      • maxMemory:33554432
      • getMemoryClass:32
    • 将VM Heap大小设置为24MB时:
      • maxMemory:25165824
      • getMemoryClass:32
  • Viewsonic GTab:
    • 将VM堆大小设置为32:
      • maxMemory:33554432
      • getMemoryClass:32
    • 将VM堆大小设置为64:
      • 最大内存:67108864
      • getMemoryClass:32

除了上面的内容,我还testing了一款运行冰淇淋三明治的Novo7 Paladin平板电脑。 这实际上是一个股票版本的ICS,除了我已经通过一个简单的过程,而不是取代整个操作系统,特别是没有提供一个接口,将允许手动调整堆大小的平板。

对于该设备,结果如下:

  • NOVO7
    • maxMemory:62914560
    • getMemoryClass:60

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

  • HTC One X
    • 最大内存:67108864
    • getMemoryClass:64

和(根据akauppi的评论):

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

根据cmcromance的评论:

  • Galaxy 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 Store Edition(4.4.2)正常
    • maxMemory:201326592
    • getMemoryClass:192
  • Galaxy S4 Play Store Edition(4.4.2)大堆
    • maxMemory:536870912
    • getMemoryClass:192

其他设备

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

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

我的期望 (这似乎是由上面的largeHeap数字支持)将是这个选项将有类似于手动设置堆通过根操作系统的效果 – 即,它会提高maxMemory()的值,同时离开getMemoryClass()单独。 还有另外一个方法getLargeMemoryClass(),它指示了使用largeHeap设置的应用程序允许有多less内存。 getLargeMemoryClass()的文档声明:“大多数应用程序不应该需要这么多的内存,而应该保留getMemoryClass()限制。”

如果我猜对了,那么使用这个选项将会有同样的好处(和危险),就像使用通过根操作系统升级堆的用户可用的空间一样(例如,如果你的应用程序使用额外的内存,它可能不会与用户同时运行的任何其他应用程序一样好玩)。

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

从上面我们可以看出,对于给定的设备/操作系统configuration, getMemoryClass()结果是不变的,而maxMemory()值在用户设置不同的堆时会改变。

我自己的实践经验是,在G1(内存类为16)上,如果手动select24MB作为堆大小,即使允许我的内存使用量向上漂移到20MB,也可以运行而不会出错高达24MB,尽pipe我还没有尝试过)。 但其他类似的大型应用程序可能会因为我自己的应用程序的臃肿而从内存中被刷新。 而且,相反,如果这些其他高维护应用程序被用户带到前台, 我的应用程序可能会从内存中被刷新。

所以,你不能超过由maxMemory()指定的内存量。 而且,您应该尽量保持在getMemoryClass()指定的限制范围内。 一种方法是,如果所有其他方法都失败了,可能会限制这些设备的function,从而节省内存。

最后,如果您打算超过getMemoryClass()指定的MB数,我的build议是在保存和恢复您的应用程序状态方面长期努力工作,以便用户的体验几乎不中断,如果onStop() / onResume()周期发生。

在我的情况下,由于性能的原因,我限制了我的应用程序运行2.2及以上的设备,这意味着几乎所有运行我的应用程序的设备将有一个24或更高的memoryClass。 所以我可以devise占用20MB的堆,并且非常有把握地相信我的应用可以和用户可能同时运行的其他应用程序一起玩。

但是总是会有一些根植的用户在较旧的设备(例如G1)上加载2.2或更高版本的Android。 当你遇到这样的configuration时,理想情况下,即使maxMemory()告诉你,你可以远远高于getMemoryClass()告诉你应该定位的16MB,你应该减less你的内存使用。 如果您无法可靠地确保您的应用能够在预算内生存,那么至less应确保onStop() / onResume()可以无缝工作。

如上面的Diane Hackborn(hackbod)所示, getMemoryClass()仅可用于API级别5(Android 2.0),因此,如她所build议的那样,可以假定任何运行早期版本的设备的物理硬件操作系统旨在优化支持占用不超过16MB的堆空间的应用程序。

相比之下,根据文档, maxMemory()可以一直返回到API级别1. maxMemory()maxMemory()在2.0以前的版本,可能会返回一个16MB的值,但是我确实看到在我的后来)CyanogenMod版本的用户可以select一个低至12MB的堆值,这可能会导致一个较低的堆限制,所以我build议你继续testingmaxMemory()值,即使是OS之前的版本2.0。 如果你需要超过maxMemory()表示允许的话,你甚至可能不得不拒绝运行这个不太可能发生的事情,这个值甚至低于16MB。

官方API是:

2.0引入了更大的内存设备。 您可以假定运行OS的早期版本的设备正在使用原始内存类(16)。

Debug.getNativeHeapSize()将做的伎俩,我应该考虑。 不过从1.0开始就一直在那里。

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

以下是你如何做到这一点:

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

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

获取您的应用程序目前使用的多less堆:

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

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

 long availableMemory=maxMemory-usedMemory; 

而且,要很好地格式化它们,你可以使用:

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

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

 Runtime.getRuntime().maxMemory() 

我使用ActivityManager.getMemoryClass(),但在CyanogenMod 7(我没有在别处testing),如果用户手动设置堆大小,它返回错误的值。

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

我在Nexus 7上做了游戏原型devise的错误,然后几乎立即在我妻子的通用4.04平板电脑(memoryclass 48,maxmemory 50331648)上发现内存不足,

当我确定内存类是低时,我将需要重新调整我的项目加载较less的资源。
Java有没有办法看到当前的堆大小? (我可以在debugging时在logCat中清楚地看到它,但是我想用一种方法在代码中看到它,比如currentheap>(maxmemory / 2)卸载高质量的位图加载低质量

你的意思是编程的,还是只是在开发和debugging? 如果是后者,你可以从Eclipse的DDMSangular度看到这个信息。 当您的模拟器(甚至可能是插入的物理电话)正在运行时,它将在左侧窗口中列出活动进程。 您可以select它,并有一个选项来跟踪堆分配。

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

价值是b

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

值是MB

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

 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 { // Памяти достаточно, выполняем операции: callback.run(); } }