Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f5b61013 authored by Jeff Brown's avatar Jeff Brown Committed by Android (Google) Code Review
Browse files

Merge "Optimize StrictMode activity instance count check."

parents b7c82637 7e442837
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -723,6 +723,8 @@ public class Activity extends ContextThemeWrapper
    
    protected static final int[] FOCUSED_STATE_SET = {com.android.internal.R.attr.state_focused};

    private final Object mInstanceTracker = StrictMode.trackActivity(this);

    private Thread mUiThread;
    final Handler mHandler = new Handler();

@@ -4552,9 +4554,6 @@ public class Activity extends ContextThemeWrapper
            mStopped = true;
        }
        mResumed = false;

        // Check for Activity leaks, if enabled.
        StrictMode.conditionallyCheckInstanceCounts();
    }

    final void performDestroy() {
+87 −16
Original line number Diff line number Diff line
@@ -1826,20 +1826,31 @@ public final class StrictMode {
    private static final HashMap<Class, Integer> sExpectedActivityInstanceCount =
            new HashMap<Class, Integer>();

    /**
     * Returns an object that is used to track instances of activites.
     * The activity should store a reference to the tracker object in one of its fields.
     * @hide
     */
    public static Object trackActivity(Object instance) {
        return new InstanceTracker(instance);
    }

    /**
     * @hide
     */
    public static void incrementExpectedActivityCount(Class klass) {
        if (klass == null || (sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
        if (klass == null) {
            return;
        }

        synchronized (StrictMode.class) {
            if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
                return;
            }

            Integer expected = sExpectedActivityInstanceCount.get(klass);
            Integer newExpected = expected == null ? 1 : expected + 1;
            sExpectedActivityInstanceCount.put(klass, newExpected);
            // Note: adding 1 here to give some breathing room during
            // orientation changes.  (shouldn't be necessary, though?)
            setExpectedClassInstanceCount(klass, newExpected + 1);
        }
    }

@@ -1847,31 +1858,48 @@ public final class StrictMode {
     * @hide
     */
    public static void decrementExpectedActivityCount(Class klass) {
        if (klass == null || (sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
        if (klass == null) {
            return;
        }

        final int limit;
        synchronized (StrictMode.class) {
            if ((sVmPolicy.mask & DETECT_VM_ACTIVITY_LEAKS) == 0) {
                return;
            }

            Integer expected = sExpectedActivityInstanceCount.get(klass);
            Integer newExpected = (expected == null || expected == 0) ? 0 : expected - 1;
            int newExpected = (expected == null || expected == 0) ? 0 : expected - 1;
            if (newExpected == 0) {
                sExpectedActivityInstanceCount.remove(klass);
            } else {
                sExpectedActivityInstanceCount.put(klass, newExpected);
            }

            // Note: adding 1 here to give some breathing room during
            // orientation changes.  (shouldn't be necessary, though?)
            setExpectedClassInstanceCount(klass, newExpected + 1);
            limit = newExpected + 1;
        }

        // Quick check.
        int actual = InstanceTracker.getInstanceCount(klass);
        if (actual <= limit) {
            return;
        }

    /**
     * @hide
     */
    public static void setExpectedClassInstanceCount(Class klass, int count) {
        synchronized (StrictMode.class) {
            setVmPolicy(new VmPolicy.Builder(sVmPolicy)
                        .setClassInstanceLimit(klass, count)
                        .build());
        // Do a GC and explicit count to double-check.
        // This is the work that we are trying to avoid by tracking the object instances
        // explicity.  Running an explicit GC can be expensive (80ms) and so can walking
        // the heap to count instance (30ms).  This extra work can make the system feel
        // noticeably less responsive during orientation changes when activities are
        // being restarted.  Granted, it is only a problem when StrictMode is enabled
        // but it is annoying.
        Runtime.getRuntime().gc();

        long instances = VMDebug.countInstancesOfClass(klass, false);
        if (instances > limit) {
            Throwable tr = new InstanceCountViolation(klass, instances, limit);
            onVmPolicyViolation(tr.getMessage(), tr);
        }
    }

@@ -2095,4 +2123,47 @@ public final class StrictMode {
            mLimit = limit;
        }
    }

    private static final class InstanceTracker {
        private static final HashMap<Class<?>, Integer> sInstanceCounts =
                new HashMap<Class<?>, Integer>();

        private final Class<?> mKlass;

        public InstanceTracker(Object instance) {
            mKlass = instance.getClass();

            synchronized (sInstanceCounts) {
                final Integer value = sInstanceCounts.get(mKlass);
                final int newValue = value != null ? value + 1 : 1;
                sInstanceCounts.put(mKlass, newValue);
            }
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                synchronized (sInstanceCounts) {
                    final Integer value = sInstanceCounts.get(mKlass);
                    if (value != null) {
                        final int newValue = value - 1;
                        if (newValue > 0) {
                            sInstanceCounts.put(mKlass, newValue);
                        } else {
                            sInstanceCounts.remove(mKlass);
                        }
                    }
                }
            } finally {
                super.finalize();
            }
        }

        public static int getInstanceCount(Class<?> klass) {
            synchronized (sInstanceCounts) {
                final Integer value = sInstanceCounts.get(klass);
                return value != null ? value : 0;
            }
        }
    }
}