如何以编程方式检测Android应用程序可用的应用程序堆大小?
我听说有一个功能可以在SDK的更高版本中执行此操作.无论如何,我正在寻找适合1.5及以上的解决方案.
下面是我检查剩余内存量的公式(不是当前堆中剩余多少内存,而是在应用程序崩溃之前可以使用多少内存).我不是很确定这是对的,是吗?
double max = Runtime.getRuntime().maxMemory(); //the maximum memory the app can use
double heapSize = Runtime.getRuntime().totalMemory(); //current heap size
double heapRemaining = Runtime.getRuntime().freeMemory(); //amount available in heap
double nativeUsage = Debug.getNativeHeapAllocatedSize(); //is this right? I only want to account for native memory that my app is being "charged" for. Is this the proper way to account for that?
//heapSize - heapRemaining = heapUsed + nativeUsage = totalUsage
double remaining = max - (heapSize - heapRemaininng + nativeUsage);
Run Code Online (Sandbox Code Playgroud) 我目前正在努力解决Android平台的奇怪行为 - Bitmap/Java堆内存限制.根据设备的不同,Android会将应用程序开发人员限制为16,24或32 MiB的Java堆空间(或者您可能会在有根电话上找到任意随机值).这可以说是相当小,但相对简单,因为我可以使用以下API测量使用情况:
Runtime rt = Runtime.getRuntime();
long javaBytes = rt.totalMemory() - rt.freeMemory();
long javaLimit = rt.maxMemory();
Run Code Online (Sandbox Code Playgroud)
很容易; 现在为了扭曲.在Android中,除少数例外的位图存储在本机堆中,不计入Java堆.谷歌的一些眼光炯炯,纯粹的开发人员认为这是"糟糕的",并允许开发者获得"超过他们的公平份额".因此,有一个很好的小代码可以计算位图和可能的其他资源所产生的本机内存使用量,并与Java堆相加,如果你去了... java.lang.OutOfMemory. 哎哟
但没什么大不了的.我有很多位图,并且不需要所有这些位图.我可以"分页"一些目前尚未使用的内容:
因此,对于尝试#1,我重构了代码,因此我可以使用try/catch包装每个位图加载:
while(true) {
try {
return BitmapFactory.decodeResource(context.getResources(), android_id, bitmapFactoryOptions);
} catch (java.lang.OutOfMemory e) {
// Do some logging
// Now free some space (the code below is a simplified version of the real thing)
Bitmap victim = selectVictim();
victim.recycle();
System.gc(); // REQUIRED; else, weird behavior ensues
}
}
Run Code Online (Sandbox Code Playgroud)
看,这是一个很好的小日志片段,显示我的代码捕获异常,并回收一些位图:
E/Epic (23221): OUT_OF_MEMORY (caught java.lang.OutOfMemory) I/Epic (23221): ArchPlatform[android].logStats() …
My app shows a list of 9 categories and each category displays a Gallery-based coverflow (graciously offered by Neil Davies here) with images of the selected category.
The images are fetched from the Web, each ranging from 300K to 500K in size, and stored in an arrayList of Drawables. This data is bound to the coverflow using a BaseAdapter (code below).
Every time I exit the coverflow and go back to the list of categories, I clear the arrayList …
我正在测试android 3.1,大堆大小的选项,大约250M的内存可用.
每当我点击应用程序首选项中的"测试"按钮时,我都会设置以下代码:
float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
bm.recycle();
bm = null;
foo = null;
Run Code Online (Sandbox Code Playgroud)
我有足够的记忆 - 我可以毫无问题地按几下按钮.
但是,如果我一直按下按钮,最终(少于20次点击)它会以OutOfMemory消失.[通常在android.graphics.Bitmap.nativeCreate(Native Method)中]
没有其他事情发生 - 我永远不必离开PreferencesActivity.当我点击按钮时,还会显示一个小Toast,因此正在进行少量其他UI活动.
这是由于碎片,还是Android Bitmap代码和/或GC中的一个可怕的错误?或者我只是在做一些愚蠢的事情?(请让它成为愚蠢的......)
有人有解决方法吗?因为上面的代码完全代表了我的代码每次用户调用它时所要做的事情,而且现在尽管细致地清除了变量,但它在几次使用后就会死掉.(这已经让我疯了很久了!)
[更新]
我已经确认这是一个碎片问题或gc错误,因为堆转储显示我在闲置(没有泄漏)时只使用5.6M处理期间大约26M达到峰值.(此外,本机堆保持在4M以下.)虽然java堆同时在我的测试设备上一直增长到280M限制,此时我开始获得OutOfMemory异常.所以我在峰值时只使用了10%的可用堆,但是获得OutOfMemory.
[不幸的是,添加对System.gc()的调用修复了我上面给出的简单测试用例.我说不幸,因为(A)它不应该有所作为,(B)因为我已经在我的实际代码中定期调用它,所以这意味着我上面的简单测试用例太简单了.
有没有其他人遇到这个?任何解决方法?有没有一种优雅的方式来重新启动我的应用程序?
[更新]
以下版本可以在3到4次调用中可靠地导致OutOfMemory(按下按钮):
float [][][]foo = new float[3][2048][2048];
Bitmap bm = Bitmap.createBitmap(2048, 2048, Bitmap.Config.ARGB_8888);
int []bar = new int[3*2048*2048];
bm.recycle();
bm = null;
System.gc();
foo = null;
System.gc();
bar = null;
System.gc();
Run Code Online (Sandbox Code Playgroud)
内存跟踪显示堆在每次调用时稳定增长,直到达到限制并死亡.如果我删除三个分配中的任何一个,它就会达到平衡并无限期地存活下来.删除除最后一个gc()之外的所有gc()会导致它稍早死亡.
我会说这是一个碎片问题,而不是gc bug本身.如果有人知道如何解决它,请告诉我.int []分配用于编写位图,因此我没有选择将其分配为2d数组(android Bitmap库的限制).
我正在为Android Honeycomb编写一个内存密集型应用程序,并且尽可能地对recycle()未使用的Bitmaps 进行了非常小心的处理.实际上,这对于应用程序来说是必要的,因为Bitmaps不断地循环进出内存.但是,我刚刚实现onConfigurationChanged()了Activity,因此(由于多种原因)我试图将内存释放例程放入其中onStop().
目前我的onStop()方法:
Views来显示默认值Drawable;recycle()对Bitmap以前使用的这些小号View秒;Bitmaps的引用.不幸的是,使用Eclipse内存分析器,它似乎对内存使用没有任何影响.
你可以想象,我已经做了很多努力,用一种名义上垃圾收集的语言来释放资源,我希望能有更多的效果.所以我的问题是:做recycle()什么?它是否实际上触发了垃圾收集,或者系统是否会保留在内存中 - 即使你调用System.gc()-until它感觉需要摆脱某些东西?
NB我知道Bitmaps实际上并没有被保存在常规堆中,但我认为调用recycle()足以确保它们被从本机堆中删除.
答案的一部分
我发现如果一个ImageView包含Bitmap已经被回收的Bitmap数据,那么数据仍会保留在内存中,直到setImageBitmap(null)被调用ImageView.甚至可能是这种情况,如果setImageResource(...)或被setImageDrawable(...)调用(它们是在一个相对较小的九个补丁中加载 - 但是,MAT分析显示这并没有删除Bitmap包含在私有成员中的大数据ImageView).简单地调用此函数onStop()已经从我们的应用程序堆中剔除了大约10MB.显然,这可能不适用于预装的Honeycomb版本的Android.
我注意到我的Galaxy Nexus android.content.res.Resources分配了大约11MB.我发现这是因为我正在使用DDMS和" Dump HPROF file"选项分析事物.所以,我花了两个小时试图查看分配是由于我的代码或支持库中的某些内容.我删除了所有数据,大量的类,我的所有库,并没有看到任何变化.在onCreate()活动方法开始时在我的代码中放置一个断点后,它显示11MB分配已经存在.
在彻底混淆之后,我决定连接我运行CM7的root用户Nook Color,看看它是针对完全相同的应用程序的初始内存使用情况报告的内容.由MAT报告的最坏情况记忆"问题可疑"的重量仅为896KB.
ICS是头重脚轻的吗?我在这里错过了什么吗?据我所知,我的应用程序运行正常,但堆使用率表示97%已满,让我担心潜在的故障.
如果它有帮助,MAT表明消耗所有内存的主要对象是Bitmaps BitmapDrawables,和NinePatchDrawables.我不明白这些分配来自何处.
我看到一个非常奇怪的问题.基本上有时大的位图内存分配将失败,即使显然有大量的内存.有很多帖子似乎问了类似的问题,但它们都与蜂窝前的安卓有关.我的理解是图像现在分配在堆上,而不是一些外部存储器.无论如何,请看下面这个日志:
10-14 13:43:53.020: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 40.637MB for 942134-byte allocation
10-14 13:43:53.070: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 126K, 11% free 41399K/46343K, paused 31ms
10-14 13:43:53.130: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 920K, 13% free 40478K/46343K, paused 30ms
10-14 13:43:53.180: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1026K, 13% free 40479K/46343K, paused 30ms
10-14 13:43:53.250: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 931K, 12% free 41193K/46343K, paused 31ms
10-14 13:43:53.250: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 41.313MB for 1048592-byte allocation
10-14 13:43:53.280: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 11% free 42217K/47431K, paused 31ms …Run Code Online (Sandbox Code Playgroud) 我正在使用下面的代码加入两个图像,但它会抛出一个OutOfMemory错误,我的图像每个大约1MB.
private Bitmap overlayMark(String first, String second)
{
Bitmap bmp1, bmp2;
bmp1 = BitmapFactory.decodeFile(first);
bmp2 = BitmapFactory.decodeFile(second);
if (bmp1 == null || bmp2 == null)
return bmp1;
int height = bmp1.getHeight();
if (height < bmp2.getHeight())
height = bmp2.getHeight();
Bitmap bmOverlay = Bitmap.createBitmap(bmp1.getWidth() + bmp2.getWidth(), height,
Bitmap.Config.ARGB_8888);// Out of memory
Canvas canvas = new Canvas(bmOverlay);
canvas.drawBitmap(bmp1, 0, 0, null);
canvas.drawBitmap(bmp2, bmp1.getWidth(), 0, null);
bmp1.recycle();
bmp2.recycle();
return bmOverlay;
}
Run Code Online (Sandbox Code Playgroud)
更新:我尝试了下面两个答案,但它仍然没有完全创建这么大的位图,问题是结果位图在2400x3200左右太大,所以它的内存不足.
如何在不耗尽内存的情况下加入大型图像?
在启用largeHeap选项之前,我正在处理大型位图,它几乎消耗了应用程序可用的整个内存,并且通过导航回收它并加载新的内存几乎可以在完整的堆上运行.但是,当某些操作需要更多内存时,应用程序崩溃.所以我启用largeHeap=true了更多的内存.
但这样做有一个意外的行为,它看起来像recycle()位图的方法不工作的大部分时间,并在58MB工作的内存(并超过有时抛出的应用程序OutOfMemoryException)现在消耗内存成倍而且还在不断增加(目前测试我确实来了231Mb分配的内存),预期的行为是内存管理继续工作,应用程序将不会使用超过60Mb.
我怎么能避免这种情况?还是有效地回收位图?
编辑:实际上,我OutOfMemoryError在设备上分配超过390Mb的内存时给了它一个.读取GC_*日志显示只有GC_FOR_ALLOC有时会释放3.8Mb,但几乎从未在其他GC运行中释放过某些内容.
我们的应用程序使用了很多位图.它适用于例如G1,XOOM.但是在HTC Desire上有一个OutOfMemory错误.在代码中我们使用try/catch(OutOfMemoryError e)并且所有设备(Desire除外)抛出异常,但HTC只是在没有OOM异常的情况下杀死应用程序.我们将位图的内存限制在12 Mb,似乎这个解决方案解决了这个问题,但客户在HTC Desire HD上仍有问题.即使有12 Mb的限制也有OOM.这是日志:
06-07 12:03:43.978 E/dalvikvm-heap( 29616):1140128-byte external allocation too large for this process.
06-07 12:03:43.978 E/dalvikvm( 29616):Out of memory: Heap Size=12311KB, Allocated=9420KB, Bitmap Size=12139KB, Limit=21884KB
06-07 12:03:43.978 E/dalvikvm( 29616):Trim info: Footprint=15751KB, Allowed Footprint=15751KB, Trimmed=3440KB
06-07 12:03:43.978 E/GraphicsJNI( 29616):VM won't let us allocate 1140128 bytes
Run Code Online (Sandbox Code Playgroud)
AFAIK设备有不同的堆大小限制(G1:16mb,Droid:24 mb,Xoom 48 mb).在我看来,系统应该至少提供16 MB,但我们有12 MB的OOM.我的问题是:如何找出Android 2的位图有多少可用堆大小?*?或者请建议如何以其他方式避免此类问题.仅供参考我们不能使用较少的位图,尤其是当它在其他设备上正常工作时.在此先感谢您的帮助!
我正在制作一个草图应用程序,主要是为了好玩.但我遇到了一个问题.
在我的应用程序中,用户可以"翻转"他们已经绘制的草图.这些草图只是400x800位图.但我经常遇到内存不足的错误.
一旦用户从它们中翻转,草图就会保存到SD,然后将它们释放出来......
if(bitmap != null)
{
bitmap.recycle();
bitmap = null;
}
Run Code Online (Sandbox Code Playgroud)
因此,在保存和取消分配之前,任何给定时间只应将一个或两个位图加载到内存中.通过一些调试打印,我已经确定这是真的(同时内存中最多两个位图).我的问题是,在14次加载/解除分配后,程序崩溃并出现OutOfMemoryError.无论我在"翻转"之间等待多久,总是14岁.
我尝试打开DDMS来查看设备的内存分配情况.奇怪的是,每次取消分配图像时,内存都会转到"未知"部分,而不是"自由"部分.这种情况一直持续到只留下一小段"空闲"内存,此时发生崩溃(并且"未知"内存变为"空闲").
经过大量的谷歌搜索后,我发现了一些资源,说明应该按预期释放位图,并且Dalvik垃圾收集总是在内存不足错误之前发生.还有一些杂项页面表明Bitmap使用的所有内存都在GC上解除分配(本机和JVM堆相似)
在这一点上,我应该注意,我正在测试两种不同的手机.摩托罗拉Droid X(股票图像,Android 2.3.3)和三星Galaxy S3(Cyanogenmod 9,Android 4.0.4).两者都表现出完全相同的行为.我的android工具是所有SDK版本的最新版本,我已经检查了一百万次,以确保我的代码中没有其他任何东西做任何奇怪的分配(几乎没有在程序启动后分配).
那么,我应该如何正确释放一个Bitmap以便它被释放(不仅仅是"未知"),我可以在其他地方使用内存?作为一种补充问题,这个"未知"的内存池是什么,它的用途是什么?
编辑:语法和拼写,在平台上添加了一些注释.
android ×12
bitmap ×6
memory ×5
heap ×2
memory-leaks ×2
android-4.0-ice-cream-sandwich ×1
dalvik ×1
ddms ×1
gallery ×1
heap-memory ×1
htc-android ×1
jvm ×1
recycle ×1