我看到一个非常奇怪的问题。基本上有时大的位图内存分配将失败,即使显然有大量的内存。有很多帖子似乎问了类似的问题,但它们都与蜂窝前的安卓有关。我的理解是图像现在分配在堆上,而不是一些外部存储器。无论如何,请看下面这个日志:
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
10-14 13:44:01.520: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 3493K, 15% free 40646K/47431K, paused 3ms+9ms
10-14 13:44:08.130: DEBUG/dalvikvm(31533): GC_EXPLICIT freed 16461K, 47% free 25527K/47431K, paused 3ms+6ms
10-14 13:44:09.150: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1007K, 45% free 26191K/47431K, paused 35ms
10-14 13:44:09.160: INFO/dalvikvm-heap(31533): Grow heap (frag case) to 29.334MB for 3850256-byte allocation
10-14 13:44:09.200: DEBUG/dalvikvm(31533): GC_CONCURRENT freed 0K, 37% free 29951K/47431K, paused 2ms+4ms
10-14 13:44:11.970: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 1878K, 38% free 29784K/47431K, paused 37ms
10-14 13:44:12.410: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed 62K, 36% free 30441K/47431K, paused 32ms
10-14 13:44:12.440: DEBUG/dalvikvm(31533): GC_FOR_ALLOC freed <1K, 32% free 32325K/47431K, paused 32ms
10-14 13:44:12.440: INFO/dalvikvm-heap(31533): Forcing collection of SoftReferences for 3850256-byte allocation
10-14 13:44:12.480: DEBUG/dalvikvm(31533): GC_BEFORE_OOM freed 124K, 33% free 32200K/47431K, paused 37ms
10-14 13:44:12.480: ERROR/dalvikvm-heap(31533): Out of memory on a 3850256-byte allocation.
我为包含如此多的日志而道歉,我希望它是相关的。我读它的方式是系统不断重新调整堆大小,直到它最终达到堆最大值。然后,我们请求一个特别大的分配失败。显然,有足够的可用内存(约15兆)。这是否意味着堆内部碎片化,并且没有足够大的连续内存段来处理我们的分配?如果是这样的话我该怎么办?如果那不是它,那么呢?
提前致谢。
答案 0 :(得分:16)
奇怪的行为是因为位图在本机堆上而不是在收集的垃圾上分配,但是android只能跟踪垃圾收集堆上的对象。从Android 2.2(或2.3或许)可以改变,如果你进行堆转储,分配的位图也是可见的。
回到问题,您的问题很可能是您手动加载的位图未正确释放。一个典型的问题是某些回调仍然设置或视图仍然引用位图。 另一个常见的问题是,如果你手动加载大位图(而不是作为资源),你将需要在它们不再需要时调用它们上的recycle(),这将从本机内存释放位图所以垃圾收藏家将能够正常工作。 (GC只能看到GC堆上的对象,并且没有哪个对象可以释放来自本机堆的内存,实际上甚至不关心它。)
我手边有这个小宝宝:
public static void stripImageView(ImageView view) {
if ( view.getDrawable() instanceof BitmapDrawable ) {
((BitmapDrawable)view.getDrawable()).getBitmap().recycle();
}
view.getDrawable().setCallback(null);
view.setImageDrawable(null);
view.getResources().flushLayoutCache();
view.destroyDrawingCache();
}
答案 1 :(得分:2)
图像从Web获取,每个图像的范围从300K到500K size,并存储在Drawables的arrayList中。
您从网络上加载的图片的kb文件大小并不直接相关。由于它们被转换为位图,因此需要为常规ARGB图像计算每个图像的宽度*高度* 4个字节。 (px中的宽度和高度)。
位图使用本机堆,通常不会在hprof中显示。 hprof应该只显示对象的数量,即剩下的BitmapDrawables或Bitmaps。
我在我的应用程序中使用此代码输出应用程序和本机堆使用的当前使用的内存:
public static void logHeap(Class clazz) {
Double allocated = new Double(Debug.getNativeHeapAllocatedSize())/new Double((1048576));
Double available = new Double(Debug.getNativeHeapSize())/1048576.0);
Double free = new Double(Debug.getNativeHeapFreeSize())/1048576.0);
DecimalFormat df = new DecimalFormat();
df.setMaximumFractionDigits(2);
df.setMinimumFractionDigits(2);
Log.d(APP, "debug. =================================");
Log.d(APP, "debug.heap native: allocated " + df.format(allocated) + "MB of " + df.format(available) + "MB (" + df.format(free) + "MB free) in [" + clazz.getName().replaceAll("com.myapp.android.","") + "]");
Log.d(APP, "debug.memory: allocated: " + df.format(new Double(Runtime.getRuntime().totalMemory()/1048576)) + "MB of " + df.format(new Double(Runtime.getRuntime().maxMemory()/1048576))+ "MB (" + df.format(new Double(Runtime.getRuntime().freeMemory()/1048576)) +"MB free)");
System.gc();
System.gc();
// don't need to add the following lines, it's just an app specific handling in my app
if (allocated>=(new Double(Runtime.getRuntime().maxMemory())/new Double((1048576))-MEMORY_BUFFER_LIMIT_FOR_RESTART)) {
android.os.Process.killProcess(android.os.Process.myPid());
}
}
我在开发期间开始或完成一项活动时调用。
logHeap(this.getClass());
以下是一些信息性链接 - 通常在这里有很多关于此主题的主题。
这也是Romain Guy(Android Framework工程师)关于软引用,弱引用,简单缓存,图像处理的有用幻灯片:http://docs.huihoo.com/google/io/2009/Th_0230_TurboChargeYourUI-HowtomakeyourAndroidUIfastandefficient.pdf
答案 2 :(得分:0)
是的,当我们使用大尺寸Bitmap时,出现“内存不足”错误。因为我有一个解决方案,通过对图像进行解码,我们可以轻松避免此错误。
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(files.getAbsolutePath());
BitmapFactory.decodeStream(fis, null, o);
fis.close();
final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
}
BitmapFactory.Options op = new BitmapFactory.Options();
op.inSampleSize = scale;
fis = new FileInputStream(files.getAbsolutePath());
bitmap[i] = BitmapFactory.decodeStream(fis, null, op);
fis.close();
答案 3 :(得分:0)
系统不会重新调整堆大小。您的应用程序具有(半)预定义堆空间。可以在某些自定义ROM中调整堆大小。这与你的问题并没有太大关系。
您看到的垃圾收集的顺序似乎表明,在GC_BEFORE_OOM发生时,您的应用程序的堆空间已用尽。 Dalvik记录器报告的统计数据可能是全系统的。