【问题标题】:Should I trust the Garbage Collector after calling Bitmap.recycle()?调用 Bitmap.recycle() 后我应该信任垃圾收集器吗?
【发布时间】:2014-11-06 01:21:06
【问题描述】:

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

这是内存监视器显示的内容:

如您所见,加载图像后,我使用了大约 60MB 的内存。当我旋转设备时,它会掉下来一点然后又回来。这让我认为没有泄漏,因为内存永远不会超过这个。

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

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

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 的使用情况,并没有什么异常。他们的阅读方式会有所不同吗?

【问题讨论】:

  • “这让我认为没有泄漏”——我建议您使用 MAT,而不是猜测。 “如果我们应该如此明智地回收我们的位图”- 在现代版本的 Android 上,您应该通过BitmapFactory.Options 上的inBitmap重用您的位图。 recycle() 有点小技巧,主要针对 Android 1.x/2.x,以帮助 GC。引用当前的Bitmap 源,“这是一个高级调用,通常不需要调用,因为当没有更多对该位图的引用时,正常的 GC 进程将释放此内存”。
  • 现在,在您的情况下,您可能不需要重用位图,这很好。如果 MAT 表明您的位图没有被强保留,它们最终将被 GC'd。这种情况发生的速度取决于多种因素,但如果没有其他因素,如果尝试分配某些内容失败,则会出现GC_FOR_ALLOC,这将收集所有可能的垃圾。
  • Re: 重用位图,我正在使用采用源位图的位图静态工厂方法,我想知道它与选项中的 inBitmap 相比如何。我也没有理由保留位图,因为我只是使用它们传递给 OpenGL,然后它们就消失了,直到我必须重新初始化视图(通过旋转等)。
  • MAT 堆中有多少是位图内存?如果您在 2.x 设备上运行该跟踪,则位图内存不会显示在 MAT 堆下。否则,差异可能只是在 MM 报告堆大小信息和 MAT 报告实际对象大小之间。
  • @Devunwired 最大的消费者(4.4 MB)实际上是com.android.content.res.Resources。然后我们进入java.lang.reflect.ArtMethod。鉴于我在屏幕上显示的视图(来自资源的 ImageViews),这一切都是有道理的。 MAT 中报告的分配比我预期的要多,奇怪的是,当 MM 明显被成功收集时,它显示的更多。

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


【解决方案1】:

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

是的。如果传入的引用被清除,垃圾收集器将在需要时获取内存(通常用于新分配)。调用 recycle() 并不能帮助这个过程或让它更快地发生。

recycle() 方法存在是因为 Bitmap 对象直到 Android 3.0 才计入堆中;因此该方法有助于协助 GC,因为它没有记录计入其堆的内存。在 3.0+ 中,内存是针对堆跟踪的,因此不再需要这种额外的簿记。

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

createBitmap() 方法将返回相同的对象,如果:

  • 源是不可变的
  • xy 都为零
  • widthheight 匹配源宽度和高度
  • 未应用任何变换矩阵

因为看起来你正在传递一个转换矩阵,所以你总是会得到一个副本,除非矩阵由于某种原因是恒等的。但同样,除非您仍然支持 2.x 版本,否则无需recycle()

【讨论】:

    猜你喜欢
    • 2010-12-15
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-14
    • 2023-03-30
    相关资源
    最近更新 更多