Android - 从低内存状态恢复

Bos*_*one 10 android memory-leaks memory-management bitmap

我正在开发一个具有非常强烈的图像处理的应用程序,其中我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
Run Code Online (Sandbox Code Playgroud)

如果我继续,上面的消息将变得更加紧迫

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
Run Code Online (Sandbox Code Playgroud)

并且不可避免地应用程序将崩溃与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;
                    }
                }
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

毋庸置疑,我在这一点上感到非常不安,并会非常感谢任何建议

dre*_*ale 10

首先,GC不会在您调用时立即回收内存.以下是android开发者网站的一些建议:

  1. 不要长期保持对上下文活动的引用(对活动的引用应该与活动本身具有相同的生命周期)

  2. 尝试使用context-application而不是context-activity

  3. 如果不控制生命周期,请避免活动中的非静态内部类,使用静态内部类并对内部活动进行弱引用.这个问题的解决方案是使用带有WeakReference的静态内部类到外部类,就像在ViewRoot及其W内部类中所做的那样

  4. 垃圾收集器不是防止内存泄漏的保险

其次,当您不太关心位图的质量时,请尝试使用BitmapFactory.options.

第三,使用try catch处理catch块中的OutOfMemory异常.

最后,使用Memory Analyzer.在Eclipse中打开DDMS,在工具栏中有一个更新堆按钮.您可以使用它来生成hprof文件,然后在andorid-sdk-tools目录中使用hprof-conv工具将文件转换为Memory Analyzer可以读取的指定格式文件.现在您可以使用Memory Analyzer来分析可能的内存泄漏.它真的是一个很好的工具,可以为你提供许多建议来避免outofmemory.

希望这会对你有所帮助,如果你找到一些更好的方法,请告诉我,我也会在我的应用程序中面对你的内存.