You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

createScaledBitmap创建Bitmap循环回收后内存持续增长问题求助

分析你遇到的Bitmap内存持续占用问题

我太懂这种困惑了——明明手动回收了Bitmap,内存却还是蹭蹭涨,onDraw结束后也不见回落,这其实是Android Bitmap管理和Canvas绘制机制的几个常见坑在搞鬼,咱们一步步拆解:

为什么内存会持续增长且不释放?

  1. Bitmap回收不是"即时生效":你调用recycle()只是告诉系统这个Bitmap可以被回收了,但Android的垃圾回收器(GC)是被动触发的,只有当内存压力达到阈值时才会运行。如果当前内存充足,GC会延迟执行,所以你看到内存没立即降下来是正常的,但如果循环几次后内存持续飙升,那就是有其他问题了。
  2. 硬件加速的"隐形缓存":你以为Canvas只有屏幕尺寸的Bitmap,但开启硬件加速时,GPU会为绘制操作创建临时纹理缓存,尤其是处理放大Bitmap这种操作时,这些GPU缓存不会随着Bitmap的recycle()一起释放,而是会被GPU暂存一段时间,导致内存占用看起来没降。
  3. onDraw循环的内存碎片问题:onDraw是UI线程调度的方法,频繁在里面创建、回收Bitmap会产生大量内存碎片,UI线程的GC触发会更保守(怕卡顿),所以内存碎片会持续累积,看起来就是内存一直在涨。

解决方法(按优先级排序)

  • 绝对不要在onDraw里循环创建Bitmap:把Bitmap的创建移到onSizeChanged()或者View初始化阶段,提前生成好需要的尺寸,复用同一个Bitmap。示例代码修改如下:
    private Bitmap mReusableScaledBitmap;
    
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        // 回收旧的Bitmap,避免内存泄漏
        if (mReusableScaledBitmap != null && !mReusableScaledBitmap.isRecycled()) {
            mReusableScaledBitmap.recycle();
        }
        // 提前创建放大后的Bitmap,按需调整尺寸
        mReusableScaledBitmap = Bitmap.createBitmap(w * 2, h * 2, Bitmap.Config.RGB_565);
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // 复用提前创建的Bitmap进行绘制
        Canvas scaledCanvas = new Canvas(mReusableScaledBitmap);
        // 在这里执行你的绘制逻辑(比如绘制原图到放大的Bitmap上)
        // ...
        // 把放大后的Bitmap绘制到当前Canvas
        canvas.drawBitmap(mReusableScaledBitmap, 0, 0, null);
    }
    
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        // View销毁时彻底回收Bitmap
        if (mReusableScaledBitmap != null && !mReusableScaledBitmap.isRecycled()) {
            mReusableScaledBitmap.recycle();
            mReusableScaledBitmap = null;
        }
    }
    
  • 用BitmapPool复用Bitmap:如果必须动态生成不同尺寸的Bitmap,试试LruBitmapPool(AndroidX提供的工具),它会缓存闲置的Bitmap,避免频繁创建和回收,减少内存开销。
  • 清理硬件缓存(按需):如果确定是硬件加速导致的内存问题,可以给这个View关闭硬件加速:
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    
    或者在绘制完成后调用destroyDrawingCache()清理View的绘制缓存,但注意不要频繁调用,会影响绘制性能。
  • 调试阶段手动触发GC:可以在循环结束后调用System.gc(),但这只是建议GC运行,不是强制,只适合调试时验证问题,上线代码别这么做,会影响性能。

调试技巧

用Android Studio的Memory Profiler查看内存分配:区分是Java堆还是Native堆的内存增长(Android 8.0之前Bitmap存在Native堆,之后移到Java堆),还能看到具体哪些对象在占用内存,精准定位问题。

内容的提问来源于stack exchange,提问作者Luke B.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:57:32