private AnalysisResult findLeakTrace( long analysisStartNanoTime, Snapshot snapshot, Instance leakingRef) { ShortestPathFinder pathFinder = new ShortestPathFinder(excludedRefs); ShortestPathFinder.Result result = pathFinder.findPath(snapshot, leakingRef); // False alarm, no strong reference path to GC Roots. if (result.leakingNode == null) { return noLeak(since(analysisStartNanoTime)); } LeakTrace leakTrace = buildLeakTrace(result.leakingNode); String className = leakingRef.getClassObj().getClassName(); // Side effect: computes retained size. snapshot.computeDominators(); Instance leakingInstance = result.leakingNode.instance; long retainedSize = leakingInstance.getTotalRetainedSize(); retainedSize += computeIgnoredBitmapRetainedSize(snapshot, leakingInstance); return leakDetected( result.excludingKnownLeaks, className, leakTrace, retainedSize, since(analysisStartNanoTime)); }
/** * Bitmaps and bitmap byte arrays are sometimes held by native gc roots, so they aren't included * in the retained size because their root dominator is a native gc root. To fix this, we check if * the leaking instance is a dominator for each bitmap instance and then add the bitmap size. * * <p>From experience, we've found that bitmap created in code (Bitmap.createBitmap()) are * correctly accounted for, however bitmaps set in layouts are not. */ private int computeIgnoredBitmapRetainedSize(Snapshot snapshot, Instance leakingInstance) { int bitmapRetainedSize = 0; ClassObj bitmapClass = snapshot.findClass("android.graphics.Bitmap"); for (Instance bitmapInstance : bitmapClass.getInstancesList()) { if (isIgnoredDominator(leakingInstance, bitmapInstance)) { ArrayInstance mBufferInstance = fieldValue(classInstanceValues(bitmapInstance), "mBuffer"); // Native bitmaps have mBuffer set to null. We sadly can't account for them. if (mBufferInstance == null) { continue; } long bufferSize = mBufferInstance.getTotalRetainedSize(); long bitmapSize = bitmapInstance.getTotalRetainedSize(); // Sometimes the size of the buffer isn't accounted for in the bitmap retained size. Since // the buffer is large, it's easy to detect by checking for bitmap size < buffer size. if (bitmapSize < bufferSize) { bitmapSize += bufferSize; } bitmapRetainedSize += bitmapSize; } } return bitmapRetainedSize; }