如何以编程方式检测Android应用程序可用的应用程序堆大小?
我听说有一个功能可以在SDK的更高版本中执行此操作。无论如何,我正在寻找适合1.5及以上的解决方案。
答案 0 :(得分:436)
有两种方法可以考虑您的短语"可用的应用堆大小":
在触发硬错误之前,我的应用程序可以使用多少堆?以及
考虑到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设备上测试了这一假设(见下文),并且我已经确认这是一种正确的解释。
对于Android的库存版本,maxMemory()
通常会返回与getMemoryClass()
中指示的大小相同的兆字节数(即,后者值的大约一百万倍)。
这两种方法可以分歧的唯一情况(我知道)是在运行Android版本的root设备上,例如CyanogenMod,它允许用户手动选择多大的a应该允许每个应用程序的堆大小。例如,在CM中,此选项显示在" CyanogenMod设置" /"表现" /" VM堆大小"。
注意:请注意手动设置此值可能会使您的系统失效,特别是如果您选择的值小于设备的正常值。
以下是我的测试结果,显示maxMemory()
和getMemoryClass()
为运行CyanogenMod的四个不同设备返回的值,每个设备使用两个不同的(手动设置)堆值:
除了上述内容外,我还在运行Ice Cream Sandwich的Novo7 Paladin平板电脑上进行了测试。 这本质上是ICS的库存版本,除了我通过一个不替换整个操作系统的简单过程来植根平板电脑,特别是没有提供允许手动调整堆大小的接口。
对于该设备,结果如下:
另外(根据以下评论中的每个Kishore):
(根据akauppi的评论):
根据cmcromance的评论:
并且(根据腾讯的评论):
其他设备
我还没有使用特殊的android测试这两种方法:largeHeap =" true"自Honeycomb以来可以使用清单选项,但由于cmcromance和腾度,我们确实有一些示例largeHeap值,如上所述。
我的期望(似乎上面的largeHeap数字支持)将是这个选项的效果类似于通过root操作系统手动设置堆 - 即,它会提高单独离开maxMemory()
时getMemoryClass()
的值。还有另一种方法getLargeMemoryClass(),它使用largeHeap设置指示应用程序允许的内存量。 getLargeMemoryClass()的文档说明,"大多数应用程序不需要这么多的内存,而应该保持getMemoryClass()限制。"
如果我猜对了,那么使用该选项将具有与使用通过root操作系统提升堆的用户可用空间相同的好处(和危险)(即,如果你的应用程序使用额外的内存,它可能不会与用户同时运行的任何其他应用程序一样好。)
请注意,内存类显然不需要是8MB的倍数。
我们可以从上面看到getMemoryClass()
结果对于给定的设备/ OS配置是不变的,而当用户以不同方式设置堆时,maxMemory()值会发生变化。
我自己的实际经验是在G1(内存等级为16)上,如果我手动选择24MB作为堆大小,即使我的内存使用率允许向20MB漂移,我也可以无错误地运行(大概它可能会高达24MB,虽然我还没试过这个)。但是,由于我自己应用程序的贪婪,其他类似的大型应用程序可能会从内存中刷新。而且,相反,如果用户将这些其他高维护应用程序带到前台,我的应用程序可能会从内存中刷新。
因此,您无法检查maxMemory()
指定的内存量。并且,您应该尝试以保持在getMemoryClass()
指定的限制范围内。一种方法是,如果所有其他方法都失败,可能会以节省内存的方式限制此类设备的功能。
最后,如果您计划超过getMemoryClass()
中指定的兆字节数,我的建议是长期努力地保存和恢复您应用的状态,以便如果发生onStop()
/ onResume()
周期,用户的体验几乎不会中断。
就我而言,出于性能原因,我将我的应用程序限制为运行2.2及更高版本的设备,这意味着运行我的应用程序的几乎所有设备都将具有24或更高的memoryClass。因此,我可以设计占用高达20MB的堆,并且非常自信我的应用程序可以与用户可能同时运行的其他应用程序一起使用。
但是总会有一些root用户将2.2或更高版本的Android加载到较旧的设备上(例如,G1)。当您遇到这样的配置时,理想情况下,您应该减少内存使用,即使maxMemory()
告诉您可以远远超过getMemoryClass()
告诉您应该定位。如果您无法可靠地确保您的应用符合预算,那么至少要确保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版本。如果您需要超过maxMemory()
表示允许的话,您甚至可能不得不拒绝在此值设置为低于16MB的情况下运行。
答案 1 :(得分:20)
官方API 是:
这是在2.0中引入的,其中出现了更大的存储设备。您可以假设运行早期版本操作系统的设备正在使用原始内存类(16)。
答案 2 :(得分:14)
Debug.getNativeHeapSize()
会做到这一点。不过,它自1.0以来一直存在。
Debug
类有许多用于跟踪分配和其他性能问题的好方法。此外,如果您需要检测内存不足的情况,请查看Activity.onLowMemory()
。
答案 3 :(得分:11)
这是你如何做到的:
获取应用可以使用的最大堆大小:
Runtime runtime = Runtime.getRuntime();
long maxMemory=runtime.maxMemory();
获取您的应用当前使用的堆数:
long usedMemory=runtime.totalMemory() - runtime.freeMemory();
获取您的应用现在可以使用多少堆(可用内存):
long availableMemory=maxMemory-usedMemory;
并且,为了很好地格式化每一个,你可以使用:
String formattedMemorySize=Formatter.formatShortFileSize(context,memorySize);
答案 4 :(得分:4)
以字节为单位返回最大堆大小:
Runtime.getRuntime().maxMemory()
我使用的是ActivityManager.getMemoryClass()但是在CyanogenMod 7上(我没有在其他地方测试过),如果用户手动设置堆大小,它会返回错误的值。
答案 5 :(得分:1)
Asus Nexus 7(2013)32Gig: getMemoryClass()= 192 maxMemory()= 201326592
我错误地在Nexus 7上对我的游戏进行原型设计,然后发现它在我妻子的通用4.04平板电脑(内存类48,maxmemory 50331648)上几乎立刻耗尽内存
当我确定内存类低时,我需要重构我的项目以加载更少的资源 在Java中有没有办法看到当前的堆大小? (我可以在调试时在logCat中清楚地看到它,但我想在代码中看到它以适应,就像currentheap>(maxmemory / 2)卸载高质量位图加载低质量
答案 6 :(得分:1)
某些操作比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();
}
}
答案 7 :(得分:0)
你的意思是编程,还是只是在你开发和调试的时候?如果是后者,您可以在Eclipse中从DDMS角度看到该信息。当您的模拟器(甚至可能是插入的物理电话)正在运行时,它将在左侧的窗口中列出活动进程。您可以选择它,并且可以选择跟踪堆分配。
答案 8 :(得分:0)
Runtime rt = Runtime.getRuntime();
rt.maxMemory()
值是b
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.getMemoryClass()
值为MB