Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android memory leak on textview - LeakCanary (Leak can be ignored)

I keep having the following memory leak as displayed by LeakCanary, when I go from my splash screen to the mainactivity. I understand that this is an expected leak due to fault in the Android OS itself, but is there a way I can avoid this (by setting specifics of some TextView somewhere?)

D/LeakCanary﹕ * LEAK CAN BE IGNORED.
D/LeakCanary﹕ * com.gmspartnersltd.earthmiles.views.ActivitySignUp_ has leaked:
D/LeakCanary﹕ * GC ROOT static android.text.TextLine.sCached
D/LeakCanary﹕ * references array android.text.TextLine[].[1]
D/LeakCanary﹕ * references android.text.TextLine.mCharacterStyleSpanSet
D/LeakCanary﹕ * references android.text.SpanSet.spans
D/LeakCanary﹕ * references array android.text.style.CharacterStyle[].[1]
D/LeakCanary﹕ * references com.gmspartnersltd.earthmiles.views.ActivitySignUp$2.this$0 (anonymous class extends android.text.style.ClickableSpan)
D/LeakCanary﹕ * leaks com.gmspartnersltd.earthmiles.views.ActivitySignUp_ instance
D/LeakCanary﹕ [ 05-22 08:54:52.160 13969:18091 D/LeakCanary ]
    * Reference Key: bb8124a9-2829-4ff3-8ded-13cf35f80f54
D/LeakCanary﹕ * Device: Genymotion generic Google Nexus 5 - 5.0.0 - API 21 - 1080x1920 vbox86p
D/LeakCanary﹕ * Android Version: 5.0 API: 21 LeakCanary: 1.3.1
D/LeakCanary﹕ * Durations: watch=10898ms, gc=137ms, heap dump=5529ms, analysis=9193ms
D/LeakCanary﹕ [ 05-22 08:54:52.160 13969:18091 D/LeakCanary ]
    * Details:
D/LeakCanary﹕ * Class android.text.TextLine
D/LeakCanary﹕ |   static $staticOverhead = byte[] [id=0x70622169;length=24;size=40]
D/LeakCanary﹕ |   static sCached = android.text.TextLine[] [id=0x70775010;length=3]
D/LeakCanary﹕ |   static DEBUG = false
D/LeakCanary﹕ |   static TAB_INCREMENT = 20
D/LeakCanary﹕ * Array of android.text.TextLine[]
D/LeakCanary﹕ |   [0] = android.text.TextLine [id=0x1309a2e0]
D/LeakCanary﹕ |   [1] = android.text.TextLine [id=0x12eed650]
D/LeakCanary﹕ |   [2] = null
D/LeakCanary﹕ * Instance of android.text.TextLine
D/LeakCanary﹕ |   static $staticOverhead = byte[] [id=0x70622169;length=24;size=40]
D/LeakCanary﹕ |   static sCached = android.text.TextLine[] [id=0x70775010;length=3]
D/LeakCanary﹕ |   static DEBUG = false
D/LeakCanary﹕ |   static TAB_INCREMENT = 20
D/LeakCanary﹕ |   mCharacterStyleSpanSet = android.text.SpanSet [id=0x12e32d80]
D/LeakCanary﹕ |   mChars = null
D/LeakCanary﹕ |   mDirections = null
D/LeakCanary﹕ |   mMetricAffectingSpanSpanSet = android.text.SpanSet [id=0x12e32d60]
D/LeakCanary﹕ |   mPaint = null
D/LeakCanary﹕ |   mReplacementSpanSpanSet = android.text.SpanSet [id=0x12e32da0]
D/LeakCanary﹕ |   mSpanned = android.text.SpannedString [id=0x132dabe0]
D/LeakCanary﹕ |   mTabs = null
D/LeakCanary﹕ |   mText = null
D/LeakCanary﹕ |   mWorkPaint = android.text.TextPaint [id=0x1300f5c0]
D/LeakCanary﹕ |   mCharsValid = false
D/LeakCanary﹕ |   mDir = 1
D/LeakCanary﹕ |   mHasTabs = false
D/LeakCanary﹕ |   mLen = 1
D/LeakCanary﹕ |   mStart = 0
D/LeakCanary﹕ * Instance of android.text.SpanSet
D/LeakCanary﹕ |   classType = java.lang.Class [id=0x703bb448;name=android.text.style.CharacterStyle]
D/LeakCanary﹕ |   spanEnds = int[] [id=0x1309fd60;length=2;size=24]
D/LeakCanary﹕ |   spanFlags = int[] [id=0x1309fda0;length=2;size=24]
D/LeakCanary﹕ |   spanStarts = int[] [id=0x1309fd20;length=2;size=24]
D/LeakCanary﹕ |   spans = android.text.style.CharacterStyle[] [id=0x1309fce0;length=2]
D/LeakCanary﹕ |   numberOfSpans = 1
D/LeakCanary﹕ * Array of android.text.style.CharacterStyle[]
D/LeakCanary﹕ |   [0] = null
D/LeakCanary﹕ |   [1] = com.gmspartnersltd.earthmiles.views.ActivitySignUp$2 [id=0x130952c0]
D/LeakCanary﹕ * Instance of com.gmspartnersltd.earthmiles.views.ActivitySignUp$2
D/LeakCanary﹕ |   this$0 = com.gmspartnersltd.earthmiles.views.ActivitySignUp_ [id=0x13361800]
D/LeakCanary﹕ * Instance of com.gmspartnersltd.earthmiles.views.ActivitySignUp_
D/LeakCanary﹕ |   onViewChangedNotifier_ = org.androidannotations.api.view.OnViewChangedNotifier [id=0x13052dc0]
D/LeakCanary﹕ |   birthday = null
D/LeakCanary﹕ |   buttonNext = android.support.v7.widget.AppCompatButton [id=0x13459c00]
D/LeakCanary﹕ |   confirmPassword = java.lang.String [id=0x132f01a0]
D/LeakCanary﹕ |   editTextConformPassword = android.support.v7.widget.AppCompatEditText [id=0x13458400]
D/LeakCanary﹕ |   editTextEmail = android.support.v7.widget.AppCompatEditText [id=0x1339b000]
D/LeakCanary﹕ |   editTextFirstName = android.support.v7.widget.AppCompatEditText [id=0x13396c00]
D/LeakCanary﹕ |   editTextLastName = android.support.v7.widget.AppCompatEditText [id=0x13398800]
D/LeakCanary﹕ |   editTextPassword = android.support.v7.widget.AppCompatEditText [id=0x13456c00]
D/LeakCanary﹕ |   email = java.lang.String [id=0x132f0040]
D/LeakCanary﹕ |   facebook = com.facebook.android.Facebook [id=0x1307ee00]
D/LeakCanary﹕ |   fbUserId = null
D/LeakCanary﹕ |   firstName = java.lang.String [id=0x132f0080]
D/LeakCanary﹕ |   gender = null
D/LeakCanary﹕ |   lastName = java.lang.String [id=0x132f00e0]
D/LeakCanary﹕ |   location = null
D/LeakCanary﹕ |   mAsyncRunner = com.facebook.android.AsyncFacebookRunner [id=0x130952a0]
D/LeakCanary﹕ |   password = java.lang.String [id=0x132f0140]
D/LeakCanary﹕ |   termsOfUse = android.support.v7.widget.AppCompatTextView [id=0x1345a000]
D/LeakCanary﹕ |   text = android.text.SpannableString [id=0x1310eaa0]
D/LeakCanary﹕ |   fromFacebook = false
D/LeakCanary﹕ |   etHelpMessage = null
D/LeakCanary﹕ |   mProgressHUD = null
D/LeakCanary﹕ |   positiveAction = null
D/LeakCanary﹕ |   showBusyAnimationRequesterCount = 0
D/LeakCanary﹕ |   mDelegate = android.support.v7.app.AppCompatDelegateImplV11 [id=0x12ecfd80]
D/LeakCanary﹕ |   mAllLoaderManagers = android.support.v4.util.SimpleArrayMap [id=0x131d80a0]
D/LeakCanary﹕ |   mContainer = android.support.v4.app.FragmentActivity$2 [id=0x13052db0]
D/LeakCanary﹕ |   mFragments = android.support.v4.app.FragmentManagerImpl [id=0x12f7cf60]
D/LeakCanary﹕ |   mHandler = android.support.v4.app.FragmentActivity$1 [id=0x1310ea80]
D/LeakCanary﹕ |   mLoaderManager = null
D/LeakCanary﹕ |   mCheckedForLoaderManager = true
D/LeakCanary﹕ |   mCreated = true
D/LeakCanary﹕ |   mLoadersStarted = false
D/LeakCanary﹕ |   mOptionsMenuInvalidated = false
D/LeakCanary﹕ |   mReallyStopped = true
D/LeakCanary﹕ |   mResumed = false
D/LeakCanary﹕ |   mRetaining = false
D/LeakCanary﹕ |   mStopped = true
D/LeakCanary﹕ |   mActionBar = null
D/LeakCanary﹕ |   mActivityInfo = android.content.pm.ActivityInfo [id=0x12db0180]
D/LeakCanary﹕ |   mActivityTransitionState = android.app.ActivityTransitionState [id=0x1304b600]
D/LeakCanary﹕ |   mAllLoaderManagers = android.util.ArrayMap [id=0x131c9d00]
D/LeakCanary﹕ |   mApplication = com.gmspartnersltd.earthmiles.globalstate.App [id=0x12c6e8c0]
D/LeakCanary﹕ |   mComponent = android.content.ComponentName [id=0x12f64150]
D/LeakCanary﹕ |   mContainer = android.app.Activity$1 [id=0x13052d70]
D/LeakCanary﹕ |   mCurrentConfig = android.content.res.Configuration [id=0x12f97520]
D/LeakCanary﹕ |   mDecor = null
D/LeakCanary﹕ |   mDefaultKeySsb = null
D/LeakCanary﹕ |   mEmbeddedID = null
D/LeakCanary﹕ |   mEnterTransitionListener = android.app.SharedElementCallback$1 [id=0x70765ba8]
D/LeakCanary﹕ |   mExitTransitionListener = android.app.SharedElementCallback$1 [id=0x70765ba8]
D/LeakCanary﹕ |   mFragments = android.app.FragmentManagerImpl [id=0x12f7cef0]
D/LeakCanary﹕ |   mHandler = android.os.Handler [id=0x1310ea60]
D/LeakCanary﹕ |   mInstanceTracker = android.os.StrictMode$InstanceTracker [id=0x13052d90]
D/LeakCanary﹕ |   mInstrumentation = android.app.Instrumentation [id=0x12c33f70]
D/LeakCanary﹕ |   mIntent = android.content.Intent [id=0x12f3b300]
D/LeakCanary﹕ |   mLastNonConfigurationInstances = null
D/LeakCanary﹕ |   mLoaderManager = null
D/LeakCanary﹕ |   mMainThread = android.app.ActivityThread [id=0x12c2b100]
D/LeakCanary﹕ |   mManagedCursors = java.util.ArrayList [id=0x1310ea40]
D/LeakCanary﹕ |   mManagedDialogs = null
D/LeakCanary﹕ |   mMenuInflater = null
D/LeakCanary﹕ |   mParent = null
D/LeakCanary﹕ |   mResultData = null
D/LeakCanary﹕ |   mSearchManager = null
D/LeakCanary﹕ |   mTitle = java.lang.String [id=0x12e6d7e0]
D/LeakCanary﹕ |   mToken = android.os.BinderProxy [id=0x12fe86a0]
D/LeakCanary﹕ |   mTranslucentCallback = null
D/LeakCanary﹕ |   mUiThread = java.lang.Thread [id=0x73b43540]
D/LeakCanary﹕ |   mVoiceInteractor = null
D/LeakCanary﹕ |   mWindow = com.android.internal.policy.impl.PhoneWindow [id=0x12e5d580]
D/LeakCanary﹕ |   mWindowManager = android.view.WindowManagerImpl [id=0x1310ed20]
D/LeakCanary﹕ |   mCalled = true
D/LeakCanary﹕ |   mChangeCanvasToTranslucent = false
D/LeakCanary﹕ |   mChangingConfigurations = false
D/LeakCanary﹕ |   mCheckedForLoaderManager = true
D/LeakCanary﹕ |   mConfigChangeFlags = 0
D/LeakCanary﹕ |   mDefaultKeyMode = 0
D/LeakCanary﹕ |   mDestroyed = true
D/LeakCanary﹕ |   mDoReportFullyDrawn = false
D/LeakCanary﹕ |   mEnableDefaultActionBarUp = false
D/LeakCanary﹕ |   mFinished = true
D/LeakCanary﹕ |   mIdent = 24993652
D/LeakCanary﹕ |   mLoadersStarted = false
D/LeakCanary﹕ |   mResultCode = 0
D/LeakCanary﹕ |   mResumed = false
D/LeakCanary﹕ |   mStartedActivity = false
D/LeakCanary﹕ |   mStopped = true
D/LeakCanary﹕ |   mTemporaryPause = false
D/LeakCanary﹕ |   mTitleColor = 0
D/LeakCanary﹕ |   mTitleReady = true
D/LeakCanary﹕ |   mVisibleBehind = false
D/LeakCanary﹕ |   mVisibleFromClient = true
D/LeakCanary﹕ |   mVisibleFromServer = false
D/LeakCanary﹕ |   mWindowAdded = true
D/LeakCanary﹕ |   mInflater = com.android.internal.policy.impl.PhoneLayoutInflater [id=0x13152580]
D/LeakCanary﹕ |   mOverrideConfiguration = null
D/LeakCanary﹕ |   mResources = android.content.res.Resources [id=0x12c33f20]
D/LeakCanary﹕ |   mTheme = android.content.res.Resources$Theme [id=0x1310ed40]
D/LeakCanary﹕ |   mThemeResource = 2131689670
D/LeakCanary﹕ |   mBase = android.app.ContextImpl [id=0x12c81100]
like image 203
dowjones123 Avatar asked May 22 '15 12:05

dowjones123


People also ask

How do I stop LeakCanary?

This is what the Android documentation recommends. Disable the leakcanary launcher activity by setting the leak_canary_add_launcher_icon resource boolean to false.

What are some best practices to avoid memory leaks on Android?

One should not use static views while developing the application, as static views are never destroyed. One should never use the Context as static, because that context will be available through the life of the application, and will not be restricted to the particular activity.

What is LeakCanary?

LeakCanary is a memory leak detection library for Android. LeakCanary's knowledge of the internals of the Android Framework gives it a unique ability to narrow down the cause of each leak, helping developers dramatically reduce Application Not Responding freezes and OutOfMemoryError crashes.

How do you identify memory leakage in an Android application?

The Memory Profiler is a component in the Android Profiler that helps you identify memory leaks and memory churn that can lead to stutter, freezes, and even app crashes. It shows a realtime graph of your app's memory use and lets you capture a heap dump, force garbage collections, and track memory allocations.


1 Answers

From AndroidExcludedRefs.java:

  // TextLine.sCached is a pool of 3 TextLine instances. TextLine.recycle() has had at least two
  // bugs that created memory leaks by not correctly clearing the recycled TextLine instances.
  // The first was fixed in android-5.1.0_r1:
  // https://github.com/android/platform_frameworks_base/commit
  // /893d6fe48d37f71e683f722457bea646994a10bf

  // The second was fixed, not released yet:
  // https://github.com/android/platform_frameworks_base/commit
  // /b3a9bc038d3a218b1dbdf7b5668e3d6c12be5ee4

  // Hack: to fix this, you could access TextLine.sCached and clear the pool every now and then
  // (e.g. on activity destroy).

Step 1: Access TextLine.sCached

public static class Utils {
    private static final Field TEXT_LINE_CACHED;

    static {
        Field textLineCached = null;
        try {
            textLineCached = Class.forName("android.text.TextLine").getDeclaredField("sCached");
            textLineCached.setAccessible(true);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        TEXT_LINE_CACHED = textLineCached;
    }

    public static void clearTextLineCache() {
        // If the field was not found for whatever reason just return.
        if (TEXT_LINE_CACHED == null) return;

        Object cached = null;
        try {
            // Get reference to the TextLine sCached array.
            cached = TEXT_LINE_CACHED.get(null);
        } catch (Exception ex) {
            //
        }
        if (cached != null) {
            // Clear the array.
            for (int i = 0, size = Array.getLength(cached); i < size; i ++) {
                Array.set(cached, i, null);
            }
        }
    }

    private Utils() {}
}

Step 2: Clear the pool

Call Utils.clearTextLineCache() when needed.

like image 142
Eugen Pechanec Avatar answered Oct 12 '22 00:10

Eugen Pechanec