I've jumped to Android project, which allocated a lot of Bitmaps . I've met there this code:

  System.gc();
  try {
    b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  } catch (OutOfMemoryError e) {
    System.gc();
    try {
      Thread.sleep(100);
    } catch (InterruptedException e1) {
      log.error("Failed to sleep.");
    }
    try {
      b = Bitmap.createBitmap(w, h, Bitmap.Config.RGB_565);
    } catch (OutOfMemoryError e1) {
      log.error("Error", e1);
    }
  }

and I've started to wonder if it have any sense to sleep Thread for a while after System.gc() was called? I've made some research about System.gc() on my own and I found few info about how it works:

Patrick Dubroy said that garbage collecting on Android >= Honeycomb it takes about 5ms (it is optimized and concurrent).

Moreover in few places I found info, that calling System.gc() is just a suggestion to run garbage collector. Above code is part of project which is enhanced for last 2 years and I suppose that this Thread.sleep is some workaround for problems which occurs on this time.

Any ideas?

有帮助吗?

解决方案

Looks like Thread.Sleep() is called to give the garbage collector enough time to do its job bebore trying to recreate the Bitmap should an OutOfMemoryError have occurred at first attempt. At second attempt, lower standards are used to consume less memory: RGB_565 instead of ARGB_8888.

It would be better (more efficient / easier to read / stronger / less dangerous for concurrent applications) to test the amount of available memory before trying to create the Bitmap the first time, rather than catching OutOfMemoryError.

Thread.sleep(100), on its side, assumes that the garbage collection will be initiated and will complete within 100ms, which is absolutely arbitrary. It may have not started yet, it may be still running...

And anyway, calling System.gc() is not helpful here as the garbage will be collected anyway. The VM detects that and won't wait should there be a lot of garbage to collect.

So my suggestion is:

  • Forget about these System.gc() and Thread.Sleep() calls. They are not efficient/helpful.
  • Test the amount of available memory before trying to create the Bitmap the first time. You can determine the size of the future resulting Bitmap as you know its size and encoding. (Refs: Runtime and ActivityManager)
  • Should the amount of memory left be low, why not reducing the standards if the resulting Bitmap fits (16 bits RGB instead of 32 bits ARGB).
  • Should this be unsufficient, logging an error and/or displaying an alert (depending on your app, I don't know the context) is fine.

Two interesting related articles from the Android team:

其他提示

Thank you Shlublu for a hint what can be a solution :) I've decided to reduce Thread.sleep two times - 50ms is enough. Moreover, I call System.gc() only at the beginning, because from what I see, memory is not allocated when createBitmap fails - so it doesn't make sense to call Garbage Collector every time when OOMException occurs. Here is the code I ended up with. From feature point of view it is just a refactoring, but maybe it will be useful for someone.

public static Bitmap allocateBitmap(int width, int height) {
 Bitmap bitmap = null;
 System.gc();
 try {
   Thread.sleep(50);
   bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
 } catch (OutOfMemoryError e8888) {
     // error log
   try {
     bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
   } catch (OutOfMemoryError e565) {
     // error log
   }
 } catch (InterruptedException e) {
   log.error("Failed to sleep", e);
 } finally {
   return bitmap;
 }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top