react native 一次内存泄漏分析
2017-02-03 12:17
232 查看
在项目中添加react native 支持后,leakcanary 检测到有内存泄漏的,如下图
可以分析出,内存泄漏是由ReactRootview 持有activity引用,在activity销毁时,ReactRootView并没有释放这个引用,导致activity无法被回收。
这个是根据log的猜测,需要跟踪代码证实这个猜测。让我们先看ReactRootView 的相关方法。
查看startReactApplication方法
mWasMeasured会在view onMeasure() 的时候赋值为true,根据view相关知识,我们知道是测量控件宽高的,这个我的这个view还没有添加到viewgroup中所以会进入这个判断,进入attachToReactInstanceManager()方法(看名字感觉也是添加到管理的操作)
这个方法中首先是一个boolean判断,这个不影响,查看attachMeasuredRootView()方法实现
重点关注代码this.mAttachedRootViews.add(rootView);这里可以看到我们的view被添加到mAttachedRootViews 的list中。而保存这个list的XReactInstanceManagerImpl实例,是在创建ReactInstanceManager 是构建。在我的项目中,我为了减少白屏时间把ReactInstanceManager用单例实现,这样就导致保存view的list一直存在内存中,引起activity内存泄漏。
代码分析证实了猜想,那么在activity onDestory()方法中把view从list中移除,应该就不会引起内存泄漏了。
我在ReactRootView 中找到unmountReactApplication()方法如下:
进入detachRootView()方法
可以看到该方法执行了this.mAttachedRootViews.remove(rootView),移除view。我修改onDestory方法如下:
添加没有在出现内存泄漏的log,我知道React Native 提供了一个实现好的ReactActivity ,于是好奇的看了一下它是如何实现onDestory的:
看到这里就明白了,fb自己的ReactActivity中也添加了unmountReactApplication方法。我估计文档中的例子都是最简单的,但是自己的项目中不可能每个activity都像例子那样去实现一个ReactInstanceManager,所以要么用系统的ReactActivity
要么自己添加unmountReactApplication方法。
可以分析出,内存泄漏是由ReactRootview 持有activity引用,在activity销毁时,ReactRootView并没有释放这个引用,导致activity无法被回收。
这个是根据log的猜测,需要跟踪代码证实这个猜测。让我们先看ReactRootView 的相关方法。
mReactRootView.startReactApplication(mReactInstanceManager, "moudle", bundle);
查看startReactApplication方法
public void startReactApplication(ReactInstanceManager reactInstanceManager, String moduleName, @Nullable Bundle launchOptions) { UiThreadUtil.assertOnUiThread(); Ass 4000 ertions.assertCondition(this.mReactInstanceManager == null, "This root view has already been attached to a catalyst instance manager"); this.mReactInstanceManager = reactInstanceManager; this.mJSModuleName = moduleName; this.mLaunchOptions = launchOptions; if(!this.mReactInstanceManager.hasStartedCreatingInitialContext()) { this.mReactInstanceManager.createReactContextInBackground(); } if(this.mWasMeasured) { this.attachToReactInstanceManager(); } }
mWasMeasured会在view onMeasure() 的时候赋值为true,根据view相关知识,我们知道是测量控件宽高的,这个我的这个view还没有添加到viewgroup中所以会进入这个判断,进入attachToReactInstanceManager()方法(看名字感觉也是添加到管理的操作)
private void attachToReactInstanceManager() { if(!this.mIsAttachedToInstance) { this.mIsAttachedToInstance = true; ((ReactInstanceManager)Assertions.assertNotNull(this.mReactInstanceManager)).attachMeasuredRootView(this); this.getViewTreeObserver().addOnGlobalLayoutListener(this.getCustomGlobalLayoutListener()); } }
这个方法中首先是一个boolean判断,这个不影响,查看attachMeasuredRootView()方法实现
public void attachMeasuredRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread(); this.mAttachedRootViews.add(rootView); if(this.mReactContextInitAsyncTask == null && this.mCurrentReactContext != null) { this.attachMeasuredRootViewToInstance(rootView, this.mCurrentReactContext.getCatalystInstance()); } }
重点关注代码this.mAttachedRootViews.add(rootView);这里可以看到我们的view被添加到mAttachedRootViews 的list中。而保存这个list的XReactInstanceManagerImpl实例,是在创建ReactInstanceManager 是构建。在我的项目中,我为了减少白屏时间把ReactInstanceManager用单例实现,这样就导致保存view的list一直存在内存中,引起activity内存泄漏。
代码分析证实了猜想,那么在activity onDestory()方法中把view从list中移除,应该就不会引起内存泄漏了。
我在ReactRootView 中找到unmountReactApplication()方法如下:
public void unmountReactApplication() { if(this.mReactInstanceManager != null && this.mIsAttachedToInstance) { this.mReactInstanceManager.detachRootView(this); this.mIsAttachedToInstance = false; } }
进入detachRootView()方法
public void detachRootView(ReactRootView rootView) { UiThreadUtil.assertOnUiThread(); if(this.mAttachedRootViews.remove(rootView) && this.mCurrentReactContext != null && this.mCurrentReactContext.hasActiveCatalystInstance()) { this.detachViewFromInstance(rootView, this.mCurrentReactContext.getCatalystInstance()); } }
可以看到该方法执行了this.mAttachedRootViews.remove(rootView),移除view。我修改onDestory方法如下:
@Override protected void onDestroy() { super.onDestroy(); if(mReactRootView!=null) { mReactRootView.unmountReactApplication(); mReactRootView = null; } if (mReactInstanceManager != null) { mReactInstanceManager.onHostDestroy(this); } }
添加没有在出现内存泄漏的log,我知道React Native 提供了一个实现好的ReactActivity ,于是好奇的看了一下它是如何实现onDestory的:
protected void onDestroy() { super.onDestroy(); this.mDelegate.onDestroy(); } protected void onDestroy() { if(this.mReactRootView != null) { this.mReactRootView.unmountReactApplication(); this.mReactRootView = null; } if(this.getReactNativeHost().hasInstance()) { this.getReactNativeHost().getReactInstanceManager().onHostDestroy(this.getPlainActivity()); } }
看到这里就明白了,fb自己的ReactActivity中也添加了unmountReactApplication方法。我估计文档中的例子都是最简单的,但是自己的项目中不可能每个activity都像例子那样去实现一个ReactInstanceManager,所以要么用系统的ReactActivity
要么自己添加unmountReactApplication方法。
相关文章推荐
- Android - 看似内存泄漏,实则不是,记一次内存泄漏的案例分析
- 记一次通过Memory Analyzer分析内存泄漏的解决过程
- 一次内存泄漏问题定位过程与分析
- 记一次内存泄漏DUMP分析
- 一次Java内存泄漏的分析
- 一次入侵秀的详细分析
- 一次死锁情况分析过程
- 做过一次需求分析后的体会
- JAVA内存泄漏分析(四)
- 一次linux服务器的入侵分析报告
- 做过一次需求分析后的体会
- 分析、调试内存泄漏的应用程序
- 一次有趣的Debug——使用Lumigent Log Explorer对SQL Server事务日志进行分析,对SQL Server事务、操作进行撤销(恢复)
- JAVA内存泄漏分析(一)
- JAVA内存泄漏分析(三)
- 从一次专业实践谈需求分析感想
- 一次诊断和解决CPU利用率高的问题分析
- JAVA内存泄漏分析(二)
- 从一次专业实践谈需求分析感想
- 我的一次入侵分析