我正在开发一个具有非常强烈的图像处理功能的应用程序,其中我在水平ListFragments
中有多个FragmentStatePagerAdapter
。我积极地采用了我能够在这里和其他地方找到的每一个技巧和建议。我下载位图并将它们保存到SD并作为软参考存储器缓存。
然而,当我在某个时刻使用应用程序时,我开始在LogCat中看到消息,就像下面的消息一样
11-05 15:57:43.951: I/dalvikvm-heap(5449): Clamp target GC heap from 32.655MB to 32.000MB
11-05 15:57:43.951: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 24% free 11512K/14983K, external 17446K/17896K, paused 64ms
11-05 15:57:44.041: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 24% free 11511K/14983K, external 17258K/17896K, paused 77ms
如果我继续,上面的消息将变得更加紧急
11-05 16:02:09.590: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 71ms
11-05 16:02:09.700: D/dalvikvm(5449): GC_EXTERNAL_ALLOC freed <1K, 23% free 11872K/15239K, external 17497K/17896K, paused 84ms
11-05 16:02:09.720: E/dalvikvm-heap(5449): 192816-byte external allocation too large for this process.
11-05 16:02:09.800: I/dalvikvm-heap(5449): Clamp target GC heap from 33.057MB to 32.000MB
11-05 16:02:09.800: D/dalvikvm(5449): GC_FOR_MALLOC freed 0K, 23% free 11872K/15239K, external 17497K/17896K, paused 68ms
11-05 16:02:09.800: E/GraphicsJNI(5449): VM won't let us allocate 192816 bytes
应用程序将不可避免地因OutOfMemoryException而崩溃
症状是经典的内存泄漏。然而在我的Fragment#onDestroy
方法中,我取消了所有待处理的任务,取消绑定并使视图无效并调用Bitmap #recycle。
有趣的是,我确实在LogCat中看到了GC调用,但即使我暂停一段时间,内存也永远不会被回收。
我的直觉是它不断重读SD中的图像会导致降级和不可避免的死亡
这是我正在使用的实用程序清理方法,试图摆脱drawables(当我说取消挂起/运行任务和清空ListView适配器时还有更多)
public static void unbindDrawables(View view, final boolean agressive) {
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i), agressive);
}
if (!AdapterView.class.isInstance(view)) {
((ViewGroup) view).removeAllViews();
}
} else {
Drawable bmp = view.getBackground();
if (bmp == null && ImageView.class.isInstance(view)) bmp = ((ImageView) view).getDrawable();
if (bmp != null) {
bmp.setCallback(null);
if (agressive && (TaggedDrawable.class.isInstance(bmp))) {
Bitmap bm = ((BitmapDrawable) bmp).getBitmap();
if (bm != null) {
if (DEBUG) Log.i(TAG, "Forcing bitmap recycle for " + bmp);
bm.recycle();
bm = null;
view.destroyDrawingCache();
view = null;
}
}
}
}
}
毋庸置疑,我在这一点上非常不安,并会非常感谢任何建议
答案 0 :(得分:10)
首先,GC不会在您调用时立即回收内存。 以下是android开发者网站的一些建议:
不要保留对上下文活动的长期引用(引用 活动应该与活动具有相同的生命周期 本身)
尝试使用context-application而不是context-activity
如果您不控制,请避免活动中的非静态内部类 他们的生命周期,使用静态内部类并做出弱引用 到里面的活动。这个问题的解决方案是使用a 静态内部类与外部类的WeakReference一样,完成后 在ViewRoot及其W内部类中,例如
垃圾收集器不能防止内存泄漏
其次,如果您不太关心位图的质量,请尝试使用BitmapFactory.options。
第三,使用try catch处理catch块中的OutOfMemory异常。
最后,使用Memory Analyzer。在Eclipse中打开DDMS,在工具栏中有一个更新堆按钮。您可以使用它来生成hprof文件,然后在andorid-sdk-tools目录中使用hprof-conv工具将文件转换为Memory Analyzer可以读取的指定格式文件。现在您可以使用Memory Analyzer来分析可能的内存泄漏。它确实是一个很好的工具,可以为您提供许多避免外存的建议。
希望这会对你有所帮助,如果你找到一些更好的方法请告诉我,我也会在我的应用程序中面对你的内存。
答案 1 :(得分:0)
尝试这一点,其中高度和宽度由你决定,目前两者都使用1024.
public class BitmapUtils {
public static Bitmap resizeBitmap( Bitmap input, int destWidth, int destHeight )
{
int srcWidth = input.getWidth();
int srcHeight = input.getHeight();
boolean needsResize = false;
float p;
if ( srcWidth > destWidth || srcHeight > destHeight ) {
needsResize = true;
if ( srcWidth > srcHeight && srcWidth > destWidth ) {
p = (float)destWidth / (float)srcWidth;
destHeight = (int)( srcHeight * p );
} else {
p = (float)destHeight / (float)srcHeight;
destWidth = (int)( srcWidth * p );
}
} else {
destWidth = srcWidth;
destHeight = srcHeight;
}
if ( needsResize ) {
Bitmap output = Bitmap.createScaledBitmap( input, destWidth, destHeight, true );
return output;
} else {
return input;
}
}
}
答案 2 :(得分:0)
由于Bitmap占用大量内存,因此不再需要在位图上调用回收。
另请参阅此Thread
如果你不调用Bitmap.recycle,只要你没有意外地保留硬引用,你就不会泄漏内存。如果您尝试一次分配太多位图或太大位图而不调用.recycle
,则可能会遇到OutOfMemoryErrors