How to avoid memory leaks from array list event listeners?

I am getting a memory leak notification via a Canary leak where it says my instance is due to the references stored by 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 a leak trace that gives an indication of a 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 encountered this before and how do you fix it?

+3


source to share


2 answers


The stack trace shows what the com.myproject.repository.myRepository

reference to com.project.newzy.dashboard.myListFragment

in the array contains eventListeners

.

I don't know exactly what yours is myRepository

, but (perhaps it is being used as Observable

), it contains a link to myListFragment

(perhaps Fragment

) what UI

needs to be destroyed,



To fix the problem, you need to make sure that when it myListFragment

is destroyed, it will no longer be part of the array eventListeners

. Just remove the listener from the array in onPause

and register it back in onResume

.

Fragment life cycle

+2


source


Your class seems to myRepository

contain a reference to the fragment instance myListFragment

.

I don't know the implementation myRepository

, but if I can make an assumption, this class is probably a Singleton class, so it will be held in memory for the entire application process. The Fragment and Activity context is a large chunk of memory, and since the Singleton class holds a reference to that memory, the Garbage Collector cannot clear the Fragment / Activity's memory at the end of its lifecycle, which means that your Fragment instance will also be in memory throughout its lifecycle. application loop, One blog about meomory leak: https://android.jlelse.eu/memory-leak-patterns-in-android-4741a7fcb570

I can recommend one solution to your problem:



  • Make your data manager dumb and inactive. Don't hook up a listener inside the data manager class. Instead, make an API to do CRUD operations on data and only connect listeners in the Fragment class. When the event is triggered, call the data manager methods appropriately. This forces your data manager not to mix logic with any particular fragment class, which you don't need to change if you need to remove the fragments later in the project.

Hope it helps.

0


source







All Articles