我有一些代码将图像加载到OpenGL纹理中。在此过程中,由于需要加载原始位图(大小适合显示)并根据EXIF数据重新定位位图,因此最终加载了3个位图。我很快在每个位图上调用.recycle()
,但是我注意到我的内存似乎没有变化。
这是内存监视器显示的内容:
如您所见,加载图像后,我使用的内存约为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:我是否可以相信垃圾收集器会在需要时收回该内存?
另外,如果我们应该如此谨慎地回收位图,为什么这么多的位图方法会说诸如“新位图可能与源是同一对象,或者可能已经制作了副本”之类的话。每次使用其他方法回收位图时,如果它是一个不同的对象,是否真的需要检查是否相等?
问题2:使用位图创建方法时,可能会或可能不会返回相同的位图或副本,是否需要检查源和输出是否相等才能回收源(如果是副本)?
编辑:
我尝试使用MAT分析此问题,并在峰值使用率(应为60 MB)下使用堆转储,但它仅报告了18.2 MB的使用率,并且没有任何异常现象。他们会以不同的方式阅读事物吗?
最佳答案
问题1:我是否可以相信垃圾收集器会在需要时收回该内存?
是。如果清除了传入的引用,则垃圾收集器将在需要时(通常用于新分配)占用内存。调用recycle()
不能帮助完成此过程,也不能使其更快地进行。
之所以存在recycle()
方法,是因为直到Android 3.0才对堆计入Bitmap
对象;因此该方法对辅助GC很有帮助,因为在其他情况下,该内存没有记录该内存的记录。在3.0及更高版本中,内存是根据堆进行跟踪的,因此不再需要额外的簿记。
问题2:使用位图创建方法时,可能会或可能不会返回相同的位图或副本,是否需要检查源和输出是否相等才能回收源(如果是副本)?createBitmap()
方法将在以下情况下返回相同的对象:
源是不可变的x
和y
均为零width
和height
匹配源宽度和高度
尚未应用转换矩阵
由于您似乎正在传递转换矩阵,因此除非矩阵由于某种原因是同一性,否则您将始终获得一个副本。但是同样,除非您仍支持2.x版本,否则不需要recycle()
。