Skip to content

Commit 547e665

Browse files
committed
Don't null the reference to Bitmap pixels until we're really ready
A change in the VM triggers a native memory error more aggressively than before, showing that there's a bug in the logic of recycling bitmaps. Since the pixel memory is allocated on the Java heap, nulling out the reference to that memory in the Java level Bitmap object can cause that memory to get collected at any time. Meanwhile, we may have a reference to that memory at the native level for rendering purposes, causing an error if/when we access that memory after it has been collected by the VM. The fix is to avoid setting the reference to the pixels to null unless we are not referring to it in native code. This is determined at the time we call recycle() - we return a boolean to indicate whether the native code is still using the memory. if not, the Java code can null out the reference and allow the VM to collect it. Otherwise, it will get collected later when the encompassing Bitmap object is collected. Issue #7339156 HTML5 tests crash the app (Vellamo) Change-Id: I3a0d6b9a6c5dd3b86cc2b0ff7719007e774b5e3c
1 parent d6e3ad5 commit 547e665

File tree

4 files changed

+31
-15
lines changed

4 files changed

+31
-15
lines changed

core/jni/android/graphics/Bitmap.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,14 +261,14 @@ static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
261261
delete bitmap;
262262
}
263263

264-
static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
264+
static jboolean Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
265265
#ifdef USE_OPENGL_RENDERER
266266
if (android::uirenderer::Caches::hasInstance()) {
267-
android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
268-
return;
267+
return android::uirenderer::Caches::getInstance().resourceCache.recycle(bitmap);
269268
}
270269
#endif // USE_OPENGL_RENDERER
271270
bitmap->setPixels(NULL, NULL);
271+
return true;
272272
}
273273

274274
// These must match the int values in Bitmap.java
@@ -665,7 +665,7 @@ static JNINativeMethod gBitmapMethods[] = {
665665
{ "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;",
666666
(void*)Bitmap_copy },
667667
{ "nativeDestructor", "(I)V", (void*)Bitmap_destructor },
668-
{ "nativeRecycle", "(I)V", (void*)Bitmap_recycle },
668+
{ "nativeRecycle", "(I)Z", (void*)Bitmap_recycle },
669669
{ "nativeCompress", "(IIILjava/io/OutputStream;[B)Z",
670670
(void*)Bitmap_compress },
671671
{ "nativeErase", "(II)V", (void*)Bitmap_erase },

graphics/java/android/graphics/Bitmap.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,14 @@ public void setLayoutBounds(int[] bounds) {
201201
*/
202202
public void recycle() {
203203
if (!mRecycled) {
204-
mBuffer = null;
205-
nativeRecycle(mNativeBitmap);
206-
mNinePatchChunk = null;
204+
if (nativeRecycle(mNativeBitmap)) {
205+
// return value indicates whether native pixel object was actually recycled.
206+
// false indicates that it is still in use at the native level and these
207+
// objects should not be collected now. They will be collected later when the
208+
// Bitmap itself is collected.
209+
mBuffer = null;
210+
mNinePatchChunk = null;
211+
}
207212
mRecycled = true;
208213
}
209214
}
@@ -1391,7 +1396,7 @@ private static native Bitmap nativeCreate(int[] colors, int offset,
13911396
private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig,
13921397
boolean isMutable);
13931398
private static native void nativeDestructor(int nativeBitmap);
1394-
private static native void nativeRecycle(int nativeBitmap);
1399+
private static native boolean nativeRecycle(int nativeBitmap);
13951400

13961401
private static native boolean nativeCompress(int nativeBitmap, int format,
13971402
int quality, OutputStream stream,

libs/hwui/ResourceCache.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -265,27 +265,38 @@ void ResourceCache::destructorLocked(SkiaColorFilter* resource) {
265265
}
266266
}
267267

268-
void ResourceCache::recycle(SkBitmap* resource) {
268+
/**
269+
* Return value indicates whether resource was actually recycled, which happens when RefCnt
270+
* reaches 0.
271+
*/
272+
bool ResourceCache::recycle(SkBitmap* resource) {
269273
Mutex::Autolock _l(mLock);
270-
recycleLocked(resource);
274+
return recycleLocked(resource);
271275
}
272276

273-
void ResourceCache::recycleLocked(SkBitmap* resource) {
277+
/**
278+
* Return value indicates whether resource was actually recycled, which happens when RefCnt
279+
* reaches 0.
280+
*/
281+
bool ResourceCache::recycleLocked(SkBitmap* resource) {
274282
ssize_t index = mCache->indexOfKey(resource);
275283
if (index < 0) {
276284
// not tracking this resource; just recycle the pixel data
277285
resource->setPixels(NULL, NULL);
278-
return;
286+
return true;
279287
}
280288
ResourceReference* ref = mCache->valueAt(index);
281289
if (ref == NULL) {
282290
// Should not get here - shouldn't get a call to recycle if we're not yet tracking it
283-
return;
291+
return true;
284292
}
285293
ref->recycled = true;
286294
if (ref->refCount == 0) {
287295
deleteResourceReferenceLocked(resource, ref);
296+
return true;
288297
}
298+
// Still referring to resource, don't recycle yet
299+
return false;
289300
}
290301

291302
/**

libs/hwui/ResourceCache.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ class ANDROID_API ResourceCache {
9999
void destructorLocked(SkiaShader* resource);
100100
void destructorLocked(SkiaColorFilter* resource);
101101

102-
void recycle(SkBitmap* resource);
103-
void recycleLocked(SkBitmap* resource);
102+
bool recycle(SkBitmap* resource);
103+
bool recycleLocked(SkBitmap* resource);
104104

105105
private:
106106
void deleteResourceReferenceLocked(void* resource, ResourceReference* ref);

0 commit comments

Comments
 (0)