From b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Tue, 7 Apr 2015 10:39:45 -0700 Subject: Prevent possible memory leak in SpanSet If SpanSet.init() is called several times in a row with different values, it is possible to change "numberOfSpans" in a way that will prevent SpanSet.recycle() from nulling out all the spans. This can lead to memory leaks of large objects through spans references. User @piwai reported this leak: com.squareup.marketfont.MarketSpan `-[1] of array android.text.style.CharacterStyle[] `-spans of object android.text.SpanSet `-mCharacterStyleSpanSet of object android.text.TextLine `-[1] of array android.text.TextLine[] `-sCached of class android.text.TextLine The MarketSpan instance is kept alive through a recycled TextLine which itself contains a SpanSet. Change-Id: Idfb2233ca16895dbe735c312662eaf0b4a2ecd65 --- core/java/android/text/SpanSet.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/java/android/text/SpanSet.java b/core/java/android/text/SpanSet.java index 3ca60338e29e..00f14939a7ad 100644 --- a/core/java/android/text/SpanSet.java +++ b/core/java/android/text/SpanSet.java @@ -17,6 +17,7 @@ package android.text; import java.lang.reflect.Array; +import java.util.Arrays; /** * A cached set of spans. Caches the result of {@link Spanned#getSpans(int, int, Class)} and then @@ -54,6 +55,7 @@ public class SpanSet { spanFlags = new int[length]; } + int prevNumberOfSpans = numberOfSpans; numberOfSpans = 0; for (int i = 0; i < length; i++) { final E span = allSpans[i]; @@ -71,6 +73,12 @@ public class SpanSet { numberOfSpans++; } + + // cleanup extra spans left over from previous init() call + if (numberOfSpans < prevNumberOfSpans) { + // prevNumberofSpans was > 0, therefore spans != null + Arrays.fill(spans, numberOfSpans, prevNumberOfSpans, null); + } } /** @@ -103,9 +111,8 @@ public class SpanSet { * Removes all internal references to the spans to avoid memory leaks. */ public void recycle() { - // The spans array is guaranteed to be not null when numberOfSpans is > 0 - for (int i = 0; i < numberOfSpans; i++) { - spans[i] = null; // prevent a leak: no reference kept when TextLine is recycled + if (spans != null) { + Arrays.fill(spans, 0, numberOfSpans, null); } } } -- cgit v1.2.3-59-g8ed1b