当前位置:网站首页>Memory leak of viewpager + recyclerview
Memory leak of viewpager + recyclerview
2022-07-05 18:21:00 【Cattle in the yard】
author : What would you like to eat today
Reprinted address :https://juejin.cn/post/7113395533460275236
Basic information
There was a question about... When optimizing memory leakage on the project RecyclerView Memory leak , The page structure is shown in the figure :
LeakCanary The captured reference chain is as follows
┬───
│ GC Root: Thread object
│
├─ java.lang.Thread instance
│ Thread name: 'main'
│ ↓ Thread.threadLocals
│ ~~~~~~~~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap instance
│ ↓ ThreadLocal$ThreadLocalMap.table
│ ~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry[] array
│ ↓ ThreadLocal$ThreadLocalMap$Entry[4]
│ ~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry instance
│ ↓ ThreadLocal$ThreadLocalMap$Entry.value
│ ~~~~~
├─ androidx.recyclerview.widget.GapWorker instance
│ ↓ GapWorker.mRecyclerViews
│ ~~~~~~~~~~~~~~
├─ java.util.ArrayList instance
│ ↓ ArrayList[0]
│ ~~~
╰→ androidx.recyclerview.widget.RecyclerView instance
Identify problems
From the reference chain, we can see that the key point is GapWorker, First look at this GapWorker
RecyclerView stay android 21 And above versions will use GapWorker
Implement the preload mechanism , stay Recyclerview Of onAttachedToWindow
Method to try to instantiate it , And pass GapWorker Of add
Methods will Recyclerview Add yourself to GapWoker
Member variables of mRecyclerViews
Go to the list , stay onDetachedFromWindow
Would call GapWorker
Of remove
Method removes its reference to itself ,GapWoker
The instance is saved in its class static member variable sGapWorker
(ThreadLocal) in , Make sure that the main thread has only one instance
RecyclerView
@Override
protected void onAttachedToWindow() {
......
if (ALLOW_THREAD_GAP_WORK) {
// from ThreadLocal In order to get GapWorker example , by null Then create a
mGapWorker = GapWorker.sGapWorker.get();
if (mGapWorker == null) {
mGapWorker = new GapWorker();
Display display = ViewCompat.getDisplay(this);
float refreshRate = 60.0f;
if (!isInEditMode() && display != null) {
float displayRefreshRate = display.getRefreshRate();
if (displayRefreshRate >= 30.0f) {
refreshRate = displayRefreshRate;
}
}
mGapWorker.mFrameIntervalNs = (long) (1000000000 / refreshRate);
// Will create the GapWorker Instance set to ThreadLocal In the middle
GapWorker.sGapWorker.set(mGapWorker);
}
// Add your own reference
mGapWorker.add(this);
}
}
@Override
protected void onDetachedFromWindow() {
......
if (ALLOW_THREAD_GAP_WORK && mGapWorker != null) {
// A reference to the exception itself
mGapWorker.remove(this);
mGapWorker = null;
}
}
final class GapWorker implements Runnable {
......
static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
public void add(RecyclerView recyclerView) {
if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
throw new IllegalStateException("RecyclerView already present in worker list!");
}
mRecyclerViews.add(recyclerView);
}
public void remove(RecyclerView recyclerView) {
boolean removeSuccess = mRecyclerViews.remove(recyclerView);
if (RecyclerView.DEBUG && !removeSuccess) {
throw new IllegalStateException("RecyclerView removal failed!");
}
}
......
GapWoker
The instance is created in the main thread ThreadLocalMap
Will be followed by a key by sGapWorker
,value For this instance Entry preservation ,GapWoker
Will not actively call sGapWorker
(ThreadLocal) Of remove
Method Entry from ThreadLocalMap
Remove , That is to say, the main thread corresponds to ThreadLocalMap
Will always hold this Entry, So this is Recyclerview
Memory leaks create conditions : as long as GapWorker.add
and GapWorker.remove
There are no paired calls , It will lead to Recyclerview
Has been GapWorker
Members of mRecyclerViews
Hold strong citation , Form a reference chain :
Thread->ThreadLocalMap->Entry(sGapWorker,GapWoker example )->mRecyclerViews->Recyclerview->Context
The next step is to find out where the problem occurred , Find... Through breakpoints Recyclerview
Of onAttachedToWindow
Method is executed twice ,onDetachedFromWindow
Method is executed only once , And that leads to this GapWorker
Of mRecyclerViews
Still keep a pair of Recyclerview
References to , So find out why onAttachedToWindow
One more execution is the answer to the question , So, usually, in the layout View Of onAttachedToWindow
When will it be called ?
ViewRootImpl
When drawing the first frame , It will be used layer by layer view OfdispatchAttachedToWindow
Method , In this method, theonAttachedToWindow
Method- Jiangzi View Add to parent ViewGroup in , And father ViewGroup Member variables of
mAttachInfo
( It's defined in View in ) Isn't empty ( staydispatchAttachedToWindow
Method ,dispatchDetachedFromWindow
Empty in method ),view OfdispatchAttachedToWindow
Will be called , And then callonAttachedToWindow
Method
From the structure of the page ,Recyclerview Belong to Fragment Of View, and Fragment Cling to ViewPager On , be Fragment The instantiation of is by ViewPager control , stay ViewPager Of onMeasure Method to load the current page Fragment
ViewPager
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
......
mInLayout = true;
//1 Instantiate the current page Fragment
populate();
mInLayout = false;
......
}
void populate() {
populate(mCurItem);
}
void populate(int newCurrentItem) {
......
if (curItem == null && N > 0) {
//2 It will call adapter Of instantiateItem Method instantiation fragment
// And will call FragmentManager.beginTransaction() Start transaction , take fragment Of attach,add And so on
curItem = addNewItem(mCurItem, curIndex);
}
......
//3 The previously generated transaction will be executed here , take fragment Of view Add to ViewPager in
mAdapter.finishUpdate(this);
......
}
}
In the third point of the code FragmentManager
Executing a transaction will Fragment Of view Add to ViewPager in , This is what I mentioned above onAttachedToWindow
The second case where the method is called .( here ViewPager Already in the drawing process ,mAttachInfo
Not empty )
Look at the project Fragment load view Code for , as follows :
In the project Fragment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_list, container, true /** The problem */)
// What needs to be noted here is LayoutInflator.infalte Of attachToRoot by true when , What is returned is the incoming root Parameters , It's just container
// Here container the truth is that ViewPager, Therefore, it is necessary to pass findViewById find R.layout.fragment_list The root of the view return
val list = view.findViewById<RecyclerView>(R.id.list)
return list
}
inflate
Methodical attachToRoot
Parameter passed true, Led to LayoutInflater
Would call root.addView()
take view Add to root( That is to say ViewPager) In the middle
LayoutInflater
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
......
if (root != null && attachToRoot) {
root.addView(temp, params);
}
......
}
ViewGroup
public void addView(View child, LayoutParams params) {
addView(child, -1, params);
}
public void addView(View child, int index, LayoutParams params) {
......
addViewInner(child, index, params, false);
}
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
......
//ViewPager Already in measure In the process ,mAttachInfo Not empty , this case Will enter
AttachInfo ai = mAttachInfo;
if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW) == 0) {
......
//child by fragment In view
child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
}
......
}
Sort out the process :ViewPager stay onMeasure
Load in Framgent,Fragment Of onCreateView
Load in view when attachToWindow
Pass on true Triggered view For the first time onAttachedToWindow
, stay Fragment After loading ,ViewPager There is no judgment view The father of View Whether for themselves , And through the FragmentManager
Once again will be view added , This triggers view The second time onAttachedToWindow
, thus Recyclerview
Two calls mGapWorker.add(this)
Add yourself to GapWoker
Of mRecyclerViews
In the middle , stay Activity Exit time ,onDetachedFromWindow
Called once , be mRecyclerViews
There is still a pair left Recyclerview
A strong reference to , This leads to memory leaks .
Solution : take true Change it to false solve the problem , Sometimes minor mistakes can waste a lot of your time
reflection
ThreadLocalMap Of Entry about Key Isn't it a weak reference ? Why does it cause memory leaks ?
From the definition of weak reference , If an object is only referenced by a weak reference , Then the object will be gc Recycling . But from GapWorker
You can see the source code of ,sGapWorker
yes static final
Decorated class static members ,sGapWorker
For what it points to ThreadLocal
Instances are strong references , And that leads to this ThreadLocalMap
Corresponding Entry Of Key It won't be gc Recycling , that ThreadLocal
Medium get
and set
Yes key by null Of Entry The removed assistive mechanism will not take effect , So in addition to actively removing Entry outside , Only after the main thread exits GapWorker
To be recycled , But it is no longer meaningful for the main thread to exit the recycle .
In that case, why Entry Of Key Also use weak references ?
hypothesis key Strong references are used , Imagine this scenario , We used the thread pool to create multiple threads , And these threads call during task execution sGapWorker
Of set
Method , These threads are cached after execution , So these threads ThreadLocalMap
Corresponding Entry Medium Key Would be right sGapWorker
Point to the ThreadLocal
Instances hold strong references , As a result, the instance cannot be recycled and a memory leak occurs , that key Using weak references can avoid this problem .
In that case, why Entry Of Value Why not use weak references ?
class Test{
static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
void A(){
sGapWorker.set(new GapWorker());
}
void B(){
GapWorker gp = sGapWorker.get()
}
}
hypothesis value It uses weak references , Imagine this scenario , First call Test
Methods A, Then it happened gc, because value Point to the GapWorker
The only object is value A weak reference to it , Then it will be recycled , At some point after that, the method was called B, Then the obtained value will be null. It can be seen that in this case value The saved values are quite unstable , Can be recycled at any time .
But because of value Strong references are used ,value The referenced object still has the possibility of memory leakage ,ThreadLocal Of set and get Methods will also be used for these key by null Of Entry Scavenging , However, there is uncertainty about the timing of such recovery , To avoid value Memory leak , We need to take the initiative to call at the appropriate time ThreadLocal
Of remove
Method to remove value References to
边栏推荐
猜你喜欢
破解湖+仓混合架构顽疾,星环科技推出自主可控云原生湖仓一体平台
Sophon KG升级3.1:打破数据间壁垒,解放企业生产力
图扑软件数字孪生 | 基于 BIM 技术的可视化管理系统
彻底理解为什么网络 I/O 会被阻塞?
Tupu software digital twin | visual management system based on BIM Technology
Maximum artificial island [how to make all nodes of a connected component record the total number of nodes? + number the connected component]
寻找第k小元素 前k小元素 select_k
图片数据不够?我做了一个免费的图像增强软件
Can communication of nano
Image classification, just look at me!
随机推荐
OpenShift常用管理命令杂记
Logical words in Articles
记一次使用Windbg分析内存“泄漏”的案例
热通孔的有效放置如何改善PCB设计中的热管理?
How to improve the thermal management in PCB design with the effective placement of thermal through holes?
吳恩達團隊2022機器學習課程,來啦
Privacy computing helps secure data circulation and sharing
Tencent music launched its new product "quyimai", which provides music commercial copyright authorization
寻找第k小元素 前k小元素 select_k
Use of print function in MATLAB
Sophon base 3.1 launched mlops function to provide wings for the operation of enterprise AI capabilities
ViewPager + RecyclerView的内存泄漏
EasyCVR平台通过接口编辑通道出现报错“ID不能为空”,是什么原因?
How to obtain the coordinates of the aircraft passing through both ends of the radar
瀚升优品app翰林优商系统开发功能介绍
[performance test] full link voltage test
Generate XML schema from class
The 2022 China Xinchuang Ecological Market Research and model selection evaluation report released that Huayun data was selected as the mainstream manufacturer of Xinchuang IT infrastructure!
星环科技数据安全管理平台 Defensor重磅发布
Introduction to Resampling