Search code examples
androidarraylistmemory-leaksandroid-memoryleakcanary

How do I avoid memory leaks from array list event listeners?


I am getting a memory leak notification via leak canary where it says my fragment instance leaks due to references held from eventlisteners and Arraylist.array. Not sure how to fix this, any ideas?

@Override
ArrayList<myInterface> getnewList() {
    ArrayList<myInterface> inst = new ArrayList<>();
    inst.addAll(myRepository.getList());
    inst.addAll(myRepository.getOtherList());
    Collections.sort(inst, myRepository.myComparator);
    return inst;
}

Here's the leak trace which gives an indication of the leak:

In com.myproject.project2.alpha.debug:3.0.0:3000000.
* com.project.newzy.dashboard.myListFragment has leaked:
* GC ROOT static com.myproject.repository.myRepository.eventListeners
* references java.util.ArrayList.array
* references array java.lang.Object[].[0]
* leaks com.project.newzy.dashboard.myListFragment instance

* Retaining: 251 KB.
* Reference Key: cc806908-52f6-42f5-be98-b39665dfa218
* Device: samsung samsung SM-J327P j3popltespr
* Android Version: 6.0.1 API: 23 LeakCanary: 1.5.1 1be44b3
* Durations: watch=5463ms, gc=131ms, heap dump=3776ms, analysis=40370ms

* Details:
* Class com.myproject.repository.myRepository
|   static eventListeners = java.util.ArrayList@587750272 (0x23085b80)
|   static Comparator = com.myproject.repository.myRepository$5@587741136 (0x230837d0)
|   static $staticOverhead = byte[40]@584327169 (0x22d42001)
|   static initialized = true
|   static lock = java.lang.Object@587741152 (0x230837e0)
|   static cache = java.util.concurrent.ConcurrentHashMap@587732480 (0x23081600)
* Instance of java.util.ArrayList
|   static $staticOverhead = byte[16]@1893860329 (0x70e203e9)
|   static MIN_CAPACITY_INCREMENT = 12
|   static serialVersionUID = 8683452581122892189
|   array = java.lang.Object[12]@591375616 (0x233fad00)
|   size = 1
|   modCount = 1
|   shadow$_klass_ = java.util.ArrayList
|   shadow$_monitor_ = 0
* Array of java.lang.Object[]
|   [0] = com.project.newzy.dashboard.myListFragment@596231392 (0x2389c4e0)
|   [1] = null
|   [2] = null
|   [3] = null
|   [4] = null
|   [5] = null
|   [6] = null
|   [7] = null
|   [8] = null
|   [9] = null
|   [10] = null
|   [11] = null
* Instance of com.project.newzy.dashboard.myListFragment
|   static $staticOverhead = byte[16]@583464961 (0x22c6f801)
|   static serialVersionUID = 0
|   static $change = null
|   adapter = com.project.newzy.dashboard.DashboardAdapter@590877408 (0x233812e0)
|   myRepository = com.myproject.repository.myRepository@587661072 (0x2306ff10)
|   inst = java.util.ArrayList@591400160 (0x23400ce0)
|   emptyLayout = android.widget.RelativeLayout@593542144 (0x2360bc00)
|   emptyMessage = android.support.v7.widget.AppCompatTextView@593544192 (0x2360c400)
|   floatingActionButton = android.support.design.widget.FloatingActionButton@594071552 (0x2368d000)
|   roomList = com.project.gui.advancedrecyclerview.AdvancedRecyclerView@593541120 (0x2360b800)
|   selectedVGroupID = null
|   listAdapter = com.project.newzy.dashboard.DashboardAdapter@590877408 (0x233812e0)
|   listDivider = com.project.newzy.base.helpers.DividerItemDecoration@589723120 (0x232675f0)
|   listManager = android.support.v7.widget.LinearLayoutManager@591052128 (0x233abd60)
|   listView = com.project.gui.advancedrecyclerview.AdvancedRecyclerView@593541120 (0x2360b800)
|   mAdded = true
|   mAnimationInfo = null
|   mArguments = null
|   mBackStackNesting = 0
|   mCalled = true
|   mCheckedForLoaderManager = true
|   mChildFragmentManager = android.support.v4.app.FragmentManagerImpl@588818688 (0x2318a900)
|   mChildNonConfig = null
|   mContainer = android.support.v4.view.ViewPager@597927936 (0x23a3a800)
|   mContainerId = 2131755178
|   mDeferStart = false
|   mDetached = false
|   mFragmentId = 2131755178
|   mFragmentManager = android.support.v4.app.FragmentManagerImpl@598589728 (0x23adc120)
|   mFromLayout = false
|   mHasMenu = false
|   mHidden = false
|   mHiddenChanged = false
|   mHost = android.support.v4.app.FragmentActivity$HostCallbacks@598610224 (0x23ae1130)
|   mInLayout = false
|   mIndex = 1
|   mInnerView = android.widget.RelativeLayout@593536000 (0x2360a400)
|   mIsNewlyAdded = false
|   mLoaderManager = null
|   mLoadersStarted = true
|   mMenuVisible = true
|   mParentFragment = null
|   mPostponedAlpha = 0.0
|   mRemoving = false
|   mRestored = false
|   mRetainInstance = false
|   mRetaining = false
|   mSavedFragmentState = null
|   mSavedViewState = null
|   mState = 5
|   mTag = java.lang.String@590080848 (0x232beb50)
|   mTarget = null
|   mTargetIndex = -1
|   mTargetRequestCode = 0
|   mUserVisibleHint = true
|   mView = android.widget.RelativeLayout@593536000 (0x2360a400)
|   mWho = java.lang.String@591143744 (0x233c2340)
|   shadow$_klass_ = com.project.newzy.dashboard.myListFragment
|   shadow$_monitor_ = -2032154546
* Excluded Refs:
| Field: android.view.inputmethod.InputMethodManager.mNextServedView
| Field: android.view.inputmethod.InputMethodManager.mServedView
| Field: android.view.inputmethod.InputMethodManager.mServedInputConnection
| Field: android.view.inputmethod.InputMethodManager.mCurRootView
| Field: android.os.UserManager.mContext
| Field: android.net.ConnectivityManager.sInstance
| Field: android.view.Choreographer$FrameDisplayEventReceiver.mMessageQueue (always)
| Thread:FinalizerWatchdogDaemon (always)
| Thread:main (always)
| Thread:LeakCanary-Heap-Dump (always)
| Class:java.lang.ref.WeakReference (always)
| Class:java.lang.ref.SoftReference (always)
| Class:java.lang.ref.PhantomReference (always)
| Class:java.lang.ref.Finalizer (always)
| Class:java.lang.ref.FinalizerReference (always)

Please let me know if you guys have faced this before and have any clues about how to go about fixing it?


Solution

  • The stack trace shows that the com.myproject.repository.myRepository is holding a reference to com.project.newzy.dashboard.myListFragment in the eventListeners array.

    I'm not sure exactly what your myRepository is but (probably it used as an Observable) it's holding a reference to an myListFragment (probably a Fragment) that the UI needs to destroy.

    To solve the issue you need to ensure that when the myListFragment is about to be destroyed it's no longer part of the eventListeners array. Just remove the listener from the array in onPause and register it back in onResume.

    Fragment Lifecycle