Home | History | Annotate | Download | only in leak
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.systemui.util.leak;
     18 
     19 import android.os.SystemClock;
     20 import android.util.ArrayMap;
     21 
     22 import java.io.PrintWriter;
     23 import java.lang.ref.Reference;
     24 import java.lang.ref.ReferenceQueue;
     25 import java.lang.ref.WeakReference;
     26 import java.util.HashSet;
     27 import java.util.Map;
     28 
     29 /**
     30  * Tracks objects that have been marked as garbage.
     31  */
     32 public class TrackedGarbage {
     33 
     34     /** Duration after which we consider garbage to be old. */
     35     private static final long GARBAGE_COLLECTION_DEADLINE_MILLIS = 60000; // 1min
     36 
     37     private final HashSet<LeakReference> mGarbage = new HashSet<>();
     38     private final ReferenceQueue<Object> mRefQueue = new ReferenceQueue<>();
     39     private final TrackedCollections mTrackedCollections;
     40 
     41     public TrackedGarbage(TrackedCollections trackedCollections) {
     42         mTrackedCollections = trackedCollections;
     43     }
     44 
     45     /**
     46      * @see LeakDetector#trackGarbage(Object)
     47      */
     48     public synchronized void track(Object o) {
     49         cleanUp();
     50         mGarbage.add(new LeakReference(o, mRefQueue));
     51         mTrackedCollections.track(mGarbage, "Garbage");
     52     }
     53 
     54     private void cleanUp() {
     55         Reference<?> ref;
     56         while ((ref = mRefQueue.poll()) != null) {
     57             mGarbage.remove(ref);
     58         }
     59     }
     60 
     61     /**
     62      * A reference to something we consider leaked if it still has strong references.
     63      *
     64      * Helpful for finding potential leaks in a heapdump: Simply find an instance of
     65      * LeakReference, find the object it refers to, then find a strong path to a GC root.
     66      */
     67     private static class LeakReference extends WeakReference<Object> {
     68         private final Class<?> clazz;
     69         private final long createdUptimeMillis;
     70 
     71         LeakReference(Object t, ReferenceQueue<Object> queue) {
     72             super(t, queue);
     73             clazz = t.getClass();
     74             createdUptimeMillis = SystemClock.uptimeMillis();
     75         }
     76     }
     77 
     78     /**
     79      * Dump statistics about the garbage.
     80      *
     81      * For each class, dumps the number of "garbage objects" that have not been collected yet.
     82      * A large number of old instances indicates a probable leak.
     83      */
     84     public synchronized void dump(PrintWriter pw) {
     85         cleanUp();
     86 
     87         long now = SystemClock.uptimeMillis();
     88 
     89         ArrayMap<Class<?>, Integer> acc = new ArrayMap<>();
     90         ArrayMap<Class<?>, Integer> accOld = new ArrayMap<>();
     91         for (LeakReference ref : mGarbage) {
     92             acc.put(ref.clazz, acc.getOrDefault(ref.clazz, 0) + 1);
     93             if (isOld(ref.createdUptimeMillis, now)) {
     94                 accOld.put(ref.clazz, accOld.getOrDefault(ref.clazz, 0) + 1);
     95             }
     96         }
     97 
     98         for (Map.Entry<Class<?>, Integer> entry : acc.entrySet()) {
     99             pw.print(entry.getKey().getName());
    100             pw.print(": ");
    101             pw.print(entry.getValue());
    102             pw.print(" total, ");
    103             pw.print(accOld.getOrDefault(entry.getKey(), 0));
    104             pw.print(" old");
    105             pw.println();
    106         }
    107     }
    108 
    109     public synchronized int countOldGarbage() {
    110         cleanUp();
    111 
    112         long now = SystemClock.uptimeMillis();
    113 
    114         int result = 0;
    115         for (LeakReference ref : mGarbage) {
    116             if (isOld(ref.createdUptimeMillis, now)) {
    117                 result++;
    118             }
    119         }
    120         return result;
    121     }
    122 
    123     private boolean isOld(long createdUptimeMillis, long now) {
    124         return createdUptimeMillis + GARBAGE_COLLECTION_DEADLINE_MILLIS < now;
    125     }
    126 }
    127