summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRomain Guy <romainguy@google.com>2010-08-06 17:58:44 -0700
committerRomain Guy <romainguy@google.com>2010-08-09 09:44:40 -0700
commit02890fd0f98b3b8d98baf0bda1ea906afd723d8b (patch)
tree72779a2551fc4a6c09c4047832d8bf1434213844
parent8576301c38381b0b06c0c804e2d638fabf408f8c (diff)
downloadframeworks_base-02890fd0f98b3b8d98baf0bda1ea906afd723d8b.zip
frameworks_base-02890fd0f98b3b8d98baf0bda1ea906afd723d8b.tar.gz
frameworks_base-02890fd0f98b3b8d98baf0bda1ea906afd723d8b.tar.bz2
Replace Bitmap's finalizers with PhantomReferences.
This change also removes the use of SoftReferences for View's drawing cache. A bitmap now creates a PhantomReference enqueued in a reference queue provided by the new Finalizers class. This queue is polled from a thread started after forking zygote. That thread is in charge of clearing the references after GC runs and of calling reclaim() on them. The reclaim() method is now how finalizers are run. Note that a PhantomReference cannot be kept in the instance it refers to, which is why they are kept in a separate List. Change-Id: If3c1a5e9dc23fa49e34857860d730f5cf5ad5926
-rw-r--r--core/java/android/util/Finalizers.java127
-rw-r--r--core/java/android/view/View.java23
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java10
-rw-r--r--graphics/java/android/graphics/Bitmap.java36
4 files changed, 171 insertions, 25 deletions
diff --git a/core/java/android/util/Finalizers.java b/core/java/android/util/Finalizers.java
new file mode 100644
index 0000000..671f2d4
--- /dev/null
+++ b/core/java/android/util/Finalizers.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import java.lang.ref.PhantomReference;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+
+/**
+ * This class can be used to implement reliable finalizers.
+ *
+ * @hide
+ */
+public final class Finalizers {
+ private static final String LOG_TAG = "Finalizers";
+
+ private static final Object[] sLock = new Object[0];
+ private static boolean sInit;
+ private static Reclaimer sReclaimer;
+
+ /**
+ * Subclass of PhantomReference used to reclaim resources.
+ */
+ public static abstract class ReclaimableReference<T> extends PhantomReference<T> {
+ public ReclaimableReference(T r, ReferenceQueue<Object> q) {
+ super(r, q);
+ }
+
+ public abstract void reclaim();
+ }
+
+ /**
+ * Returns the queue used to reclaim ReclaimableReferences.
+ *
+ * @return A reference queue or null before initialization
+ */
+ public static ReferenceQueue<Object> getQueue() {
+ synchronized (sLock) {
+ if (!sInit) {
+ return null;
+ }
+ if (!sReclaimer.isRunning()) {
+ sReclaimer = new Reclaimer(sReclaimer.mQueue);
+ sReclaimer.start();
+ }
+ return sReclaimer.mQueue;
+ }
+ }
+
+ /**
+ * Invoked by Zygote. Don't touch!
+ */
+ public static void init() {
+ synchronized (sLock) {
+ if (!sInit && sReclaimer == null) {
+ sReclaimer = new Reclaimer();
+ sReclaimer.start();
+ sInit = true;
+ }
+ }
+ }
+
+ private static class Reclaimer extends Thread {
+ ReferenceQueue<Object> mQueue;
+
+ private volatile boolean mRunning = false;
+
+ Reclaimer() {
+ this(new ReferenceQueue<Object>());
+ }
+
+ Reclaimer(ReferenceQueue<Object> queue) {
+ super("Reclaimer");
+ setDaemon(true);
+ mQueue = queue;
+ }
+
+ @Override
+ public void start() {
+ mRunning = true;
+ super.start();
+ }
+
+ boolean isRunning() {
+ return mRunning;
+ }
+
+ @SuppressWarnings({"InfiniteLoopStatement"})
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ try {
+ cleanUp(mQueue.remove());
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Reclaimer thread exiting: ", e);
+ } finally {
+ mRunning = false;
+ }
+ }
+
+ private void cleanUp(Reference<?> reference) {
+ do {
+ reference.clear();
+ ((ReclaimableReference<?>) reference).reclaim();
+ } while ((reference = mQueue.poll()) != null);
+ }
+ }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7c644e4..735b35a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1828,8 +1828,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
private int[] mDrawableState = null;
- private SoftReference<Bitmap> mDrawingCache;
- private SoftReference<Bitmap> mUnscaledDrawingCache;
+ private Bitmap mDrawingCache;
+ private Bitmap mUnscaledDrawingCache;
/**
* When this view has focus and the next focus is {@link #FOCUS_LEFT},
@@ -6916,8 +6916,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
buildDrawingCache(autoScale);
}
- return autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
- (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+ return autoScale ? mDrawingCache : mUnscaledDrawingCache;
}
/**
@@ -6932,13 +6931,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public void destroyDrawingCache() {
if (mDrawingCache != null) {
- final Bitmap bitmap = mDrawingCache.get();
- if (bitmap != null) bitmap.recycle();
+ mDrawingCache.recycle();
mDrawingCache = null;
}
if (mUnscaledDrawingCache != null) {
- final Bitmap bitmap = mUnscaledDrawingCache.get();
- if (bitmap != null) bitmap.recycle();
+ mUnscaledDrawingCache.recycle();
mUnscaledDrawingCache = null;
}
}
@@ -6999,8 +6996,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
*/
public void buildDrawingCache(boolean autoScale) {
if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || (autoScale ?
- (mDrawingCache == null || mDrawingCache.get() == null) :
- (mUnscaledDrawingCache == null || mUnscaledDrawingCache.get() == null))) {
+ mDrawingCache == null : mUnscaledDrawingCache == null)) {
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
@@ -7033,8 +7029,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
}
boolean clear = true;
- Bitmap bitmap = autoScale ? (mDrawingCache == null ? null : mDrawingCache.get()) :
- (mUnscaledDrawingCache == null ? null : mUnscaledDrawingCache.get());
+ Bitmap bitmap = autoScale ? mDrawingCache : mUnscaledDrawingCache;
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
Bitmap.Config quality;
@@ -7066,9 +7061,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility
bitmap = Bitmap.createBitmap(width, height, quality);
bitmap.setDensity(getResources().getDisplayMetrics().densityDpi);
if (autoScale) {
- mDrawingCache = new SoftReference<Bitmap>(bitmap);
+ mDrawingCache = bitmap;
} else {
- mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap);
+ mUnscaledDrawingCache = bitmap;
}
if (opaque && translucentWindow) bitmap.setHasAlpha(false);
} catch (OutOfMemoryError e) {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 59600dc..5767832 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -24,6 +24,7 @@ import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
import android.util.Config;
+import android.util.Finalizers;
import android.util.Log;
import android.util.Slog;
@@ -141,6 +142,12 @@ public class RuntimeInit {
Debug.enableEmulatorTraceOutput();
}
+ /**
+ * Initialize the thread used to reclaim resources without
+ * going through finalizers.
+ */
+ Finalizers.init();
+
initialized = true;
}
@@ -331,9 +338,6 @@ public class RuntimeInit {
}
}
- /** Counter used to prevent reentrancy in {@link #reportException}. */
- private static final AtomicInteger sInReportException = new AtomicInteger();
-
/**
* Set the object identifying this application/process, for reporting VM
* errors.
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 537dd3a..d9ee3ec 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -19,12 +19,16 @@ package android.graphics;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.DisplayMetrics;
+import android.util.Finalizers;
import java.io.OutputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
public final class Bitmap implements Parcelable {
/**
@@ -55,7 +59,7 @@ public final class Bitmap implements Parcelable {
private static volatile Matrix sScaleMatrix;
private static volatile int sDefaultDensity = -1;
-
+
/**
* For backwards compatibility, allows the app layer to change the default
* density when running old apps.
@@ -81,8 +85,7 @@ public final class Bitmap implements Parcelable {
This can be called from JNI code.
*/
- private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk,
- int density) {
+ private Bitmap(int nativeBitmap, boolean isMutable, byte[] ninePatchChunk, int density) {
if (nativeBitmap == 0) {
throw new RuntimeException("internal error: native bitmap is 0");
}
@@ -94,6 +97,13 @@ public final class Bitmap implements Parcelable {
if (density >= 0) {
mDensity = density;
}
+
+ // If the finalizers queue is null, we are running in zygote and the
+ // bitmap will never be reclaimed, so we don't need to run our native
+ // destructor
+ if (Finalizers.getQueue() != null) {
+ new BitmapFinalizer(this);
+ }
}
/**
@@ -1016,12 +1026,22 @@ public final class Bitmap implements Parcelable {
nativePrepareToDraw(mNativeBitmap);
}
- @Override
- protected void finalize() throws Throwable {
- try {
+ private static class BitmapFinalizer extends Finalizers.ReclaimableReference<Bitmap> {
+ private static final Set<BitmapFinalizer> sFinalizers = Collections.synchronizedSet(
+ new HashSet<BitmapFinalizer>());
+
+ private int mNativeBitmap;
+
+ BitmapFinalizer(Bitmap b) {
+ super(b, Finalizers.getQueue());
+ mNativeBitmap = b.mNativeBitmap;
+ sFinalizers.add(this);
+ }
+
+ @Override
+ public void reclaim() {
nativeDestructor(mNativeBitmap);
- } finally {
- super.finalize();
+ sFinalizers.remove(this);
}
}