调用Bitmap.recycle()后我应该信任垃圾收集器吗?

时间:2014-11-06 01:21:07

标签: android memory memory-leaks bitmap garbage-collection

我有一些代码将图像加载到OpenGL纹理中。在这个过程中,我最终加载了3个位图,因为我需要加载原始位图(适合显示尺寸)并根据EXIF数据重新定位位图。我很快就在每个位图上调用.recycle(),但我注意到我的记忆似乎没有改变。

以下是内存监视器显示的内容:

memory monitor

正如您所看到的,在加载图像后我使用了大约60MB的内存。当我旋转稍微下降的设备然后重新启动。这让我觉得没有泄漏,因为记忆永远不会超过它。

当我单击内存分析器中的GC按钮时,我的内存占用量急剧下降到大约8 MB。这是有道理的,因为在此过程中创建的三个位图被回收,因此可以进行垃圾回收。然后你可以看到,当我再次旋转并重建活动时,内存会向右跳回来。

这是我的代码,向您展示为什么会创建如此多的位图以及何时回收这些位图。

void layoutImage() {
    ...    
    Bitmap bitmap = loadOrientedConstrainedBitmapWithBackouts(...);
    imageTexture = new GLTexture(bitmap);
    bitmap.recycle(); // recycle bitmap 2
}

Bitmap loadOrientedConstrainedBitmapWithBackouts(Context context, Uri uri, int maxSize) {
    ...
    Bitmap bitmap = loadBitmapWithBackouts(context, uri, sampleSize); // create bitmap 1
    ...
    Bitmap out = orientBitmap(bitmap, orientation); // create bitmap 2
    bitmap.recycle(); // recycle bitmap 1
    return out;
}

Bitmap orientBitmap(Bitmap source, int orientation) {
    ...
    return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight, matrix, true);  // create bitmap 3
}

我不确定这是一个问题,可以这么说,因为记忆没有攀爬(所以没有泄漏),但是当它保持如此之高时我很好奇。由于强制垃圾收集清除它就好了,我是否应该假设如果系统需要该内存,它将在下一次GC通过时收集?它一直在运行,我一直在写这个,并且仍然可以舒适地坐在60 MB。

问题1 :我可以相信垃圾收集器会在需要时将内存恢复吗?

另外,如果我们应该如此明智地回收我们的位图,为什么这么多Bitmap方法会说“新位图可能与源相同,或者可能已经制作了副本”。如果它是一个不同的对象,每次使用这些方法来回收位图时,我是否真的必须检查相等性?

问题2 :当使用位图创建方法时,可能会也可能不会返回相同的位图或副本,我是否需要检查源和输出相等性以回收源(如果它是副本)?

编辑:

我尝试用MAT分析这个问题,在使用高峰时使用堆转储(应该是60 MB),但它只报告18.2 MB的使用情况并且看起来没什么异常。他们会以不同的方式阅读事物吗?

enter image description here

1 个答案:

答案 0 :(得分:3)

  

问题1:我可以相信垃圾收集器会在需要时将内存恢复吗?

是。如果传入的引用被清除,垃圾收集器将在需要时占用内存(通常用于新的分配)。致电recycle()并不能帮助您完成此过程,也不会让它更快发生。

存在recycle()方法,因为在Android 3.0之前,Bitmap个对象不计入堆中;因此,该方法有助于GC的帮助,因为它没有记录该堆内存的内存记录。在3.0+中,内存是针对堆进行跟踪的,因此不再需要这些额外的簿记。

  

问题2:当使用位图创建方法时,可能会或可能不会返回相同的位图或副本,如果它是副本,我是否需要检查源和输出相等性以回收源?

createBitmap()方法将在以下情况下返回相同的对象:

  • 来源是不可变的
  • xy均为零
  • widthheight匹配来源宽度和高度
  • 未应用转换矩阵

由于看起来你正在传递变换矩阵,所以除非矩阵是出于某种原因的身份,否则你将始终获得副本。但同样,除非你仍然支持2.x版本,否则不需要recycle()