如何避免数组列表事件侦听器的内存泄漏?

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

我通过 leak canary 收到内存泄漏通知,它说我的片段实例由于事件监听器和 Arraylist.array 持有的引用而泄漏。不确定如何解决这个问题,有什么想法吗?

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

这是指示泄漏的泄漏痕迹:

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@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)

如果你们以前遇到过这个问题并且有任何关于如何解决它的线索,请告诉我?

堆栈跟踪显示 com.myproject.repository.myRepositoryeventListeners 数组中持有对 com.project.newzy.dashboard.myListFragment 的引用。

我不确定你的 myRepository 到底是什么,但是(可能它用作 Observable)它持有对 myListFragment 的引用(可能是 Fragment) UI 需要销毁。

要解决此问题,您需要确保当 myListFragment 即将被销毁时,它不再是 eventListeners 数组的一部分。只需从 onPause 中的数组中删除侦听器并将其重新注册到 onResume.

您的 myRepository class 似乎包含对片段实例 myListFragment 的引用。

我不知道 myRepository 的实现,但如果我能猜到的话,这个 class 可能是一个 Singleton class 所以它在整个应用程序过程中驻留在内存中. Fragment 和 Activity 上下文是一个很大的内存块,并且由于 Singleton class 持有对该内存的引用,因此垃圾收集器在完成其生命周期时无法清除 Fragment/Activity 内存,这意味着您的 Fragment 实例也将在整个应用程序生命周期中驻留在内存中。一篇关于内存泄漏的博客:https://android.jlelse.eu/memory-leak-patterns-in-android-4741a7fcb570

我可以推荐一种解决方法来解决你的问题:

  1. 让您的数据管理器 class 变得愚蠢和无状态。不要在数据管理器中附加监听器 class。相反,make API 对数据进行 CRUD 操作,并且只在 Fragment class 中附加监听器。当事件被触发时,相应地调用数据管理器的方法。这使您的数据管理器不会将逻辑与任何特定的片段混合 class,如果您需要在项目的后期删除片段,则无需更改任何内容。

希望对您有所帮助。