可以从常见出问题场景、检测方案等方面回答。
更多问答 >>
-
2019-04-11 18:34
-
2019-05-13 08:30
-
2019-05-17 00:38
-
讨论 | Flutter Kotlin 如果二选一学习,你会怎么选?
2019-05-16 09:04 -
每日一问 | 并发专题 volatile,synchronize,cas,happens before, lost wake up
2019-05-19 20:37 -
2019-05-13 23:38

内存泄漏的根本原因是一个长生命周期对象持有一个短生命周期对象,造成短生命周期对象没有办法被回收所导致的。
常见的有:
1.内部类形式使用handler同时发了延迟消息,这时候退出activity会造成activity内存泄漏。AsyncTask同理会造成内存泄漏。
2.单例类持有activity会造成activity内存泄漏。
3.各种注册操作没有对应反注册(广播,eventbus等)。
我们分析内存泄漏的时候一定要想清楚整体的引用链关系,根据根节点可达性算法一个GCROOT持有activty那么就会造成其内存泄漏,比如上述handler的情况,handler内部类持有外部类引用,sendmessage的时候message会持有handler,enqueueMessage会造成messageQueue持有message,而如果发的是延迟消息那么message并不会立即的遍历出来处理而是阻塞到对应的message触发时间以后再处理,那么阻塞的这段时间中activity关闭就会造成内存泄漏。
整体的引用链关系就是activity->handler->message->messagequeue(GCROOT)
同理asyncTask也是,由于asyncTask中任务都是串行执行的,如果某一个任务比较耗时或有太多的任务需要处理,那么就有可能发生内存泄漏。
整体的引用链关系就是activity->asynctask->Thread(GCROOT)
解决的方法如上就是将内部类定义为静态内部类,但是静态内部类引用不到外部类的非静态属性和方法,所以可以用弱引用持有外部类,通过弱引用去调用外部类的属性和方法,同时由于弱引用自身如果只有一个对象持有它则在GC时会被直接回收的特性,所以不用担心有内存泄漏的风险。
检测的话LeakCanary可以检测,源码网上有很多文章,大家有兴趣自己看一下吧~
以上是我个人想法,欢迎一起探讨哈~
所以可以用弱引用持有外部类,这句话不应该是弱引用持有内部类吗
内部类为静态,外部类对象通过某种方式传入,然后在内部类中使用弱引用持有该对象。应该是这样
为什么要弱引用持有呢,写的时候大体写成注册,和反注册的形势,不用的时候反注册一下,handler这中的在activity销毁时,去掉所有发送的方法和延时任务就行啊 ...查看更多
为什么要弱引用持有呢,写的时候大体写成注册,和反注册的形势,不用的时候反注册一下,handler这中的在activity销毁时,去掉所有发送的方法和延时任务就行啊
我们的目的是在静态内部类中调用到外部类的属性和方法,但是静态内部类只能通过静态引用调用到外部类,如果用静态的强引用持有外部类势必会造成外部类内存泄漏,因为静态对象是GCROOT。所以用静态的弱引用持有 ...查看更多
我们的目的是在静态内部类中调用到外部类的属性和方法,但是静态内部类只能通过静态引用调用到外部类,如果用静态的强引用持有外部类势必会造成外部类内存泄漏,因为静态对象是GCROOT。所以用静态的弱引用持有,当这个Activity关闭时如果它最后只被这个弱引用持有则触发gc后这个Activity会被回收掉
这种没有任何问题,但前提是你能保证每次用handler的时候都在Activity的onDestory中调用方法去移除所有跟当前handler关联的消息
在Android程序开发中,当一个对象已经不需要再使用了,本该被回收时,而另外一个正在使用的对象持有它的引用从而导致它不能被回收,这就导致本该被回收的对象不能被回收而停留在堆内存中,内存泄漏就产生了。
内存泄露的危害:只有一个,那就是虚拟机占用内存过高,导致OOM(内存溢出),程序出错。对于Android应用来说,就是你的用户打开一个Activity,使用完之后关闭它,内存泄露;又打开,又关闭,又泄露;几次之后,程序占用内存超过系统限制。
1、单例造成的内存泄漏。
2、非静态内部类创建静态实例造成的内存泄漏。
3、Handler造成的内存泄漏。
4、线程造成的内存泄漏。
5、资源对象没关闭造成的内存泄漏。
6、Bitmap没有回收导致的内存溢出
bitmap 自己会回收的吧
补充:
Application, Activity, Fragment, View之类有生命周期回调方法的,用static修饰很危险,如果万不得已,要在它们被回收前置空;Cursor, Input、OutputStream之类的用完后一定要及时关闭,操作这些对象,close方法尽量放在finally块,防止出异常后不能执行;内存泄漏的场景: 很多人使用Webview都喜欢采用布局引用方式, 这其实也是作为内存泄漏的一个隐患,
当Activity被关闭时,Webview不会被GC马上回收,而是提交给事务,进行队列处理, 这样就造成了内存泄漏, 导致Webview无法及时回收.
目前我知道的比较安全解决方案:
1.采用布局中动态添加Webview的方式,
2.在Activity的onDestroyed方法中,先对webview进行移除所有回调,并清除所有javascriptinterface操作
然后移除通过父布局移除webview.
本人技术深度有限,如若大佬有更深层次的理解 ,可以进行补充!
火钳刘明
火钳刘明
本站第一道正式提问,决定还是慢慢运营中修复体验问题...
每天的题目不会特别简单,可能一时间回答补上来,希望大家可以针对问题,最终以学习+总结的方式学习,最后可以将总结作为答案提交过来。
每个人角度不同,答案相互可以借鉴,最终展示结果会根据点赞来排名。
希望大家都能在交流中持续成长,好的学习氛围需要大家携手共建!
内存泄露的本质是:本该回收的资源没有及时回收。
常见场景
1、活动中使用多线程,活动退出时,多线程任务没有及时回收。
2、活动中注册的系统级别的服务或者监听,在不需要使用时没有及时注销。
3、单例模式,如果传入的context是Activity级别的。如果单例没有释放,那么Activity也不会释放。
4、非静态内部类(成员内部类,局部内部类,匿名内部类)会持有对外部类的引用,导致内存溢出。
首先我们要从什么是“内存泄露”出发,内存泄露:是指本该回收的对象,因为被其他对象引用而导致无法回收。
其次我们要从GC是如何回收出发,GC回收算法有计数计数法和可达性分析算法等构成。计数计算法:当对象被引用+1,没有被引用-1,0标识GC可以回收了。可达性分析算法是指从GCRoot自上往下形搜索的路径构成的引用链,判断是否与引用链是否连接来判断。所以总结就是按照引用或链接关系来判定内存泄露情况。
因此这就能够明白了出问题场景,如上面同学所列出的问题。拿个最经典的Handler举例,经常有结论说一个Handler对应一个Loop和MessageQueue队列,那么消息机制是如何关联的呢,我们可以看到当Handler发出消息时,当msg加入到MessageQueue时,会有msg.target = this,那么当Activity退出时,此时消息队列的Handler持有引用而导致内存泄露。
一般会利用LeakCanary来检测Activity,当我们查看其源码是在Activity创建时ReferenceQueque存储该引用,当onDestroy()时,我们会将activity放入WeakReference,与ReferenceQueque关联进行判断。
1.生命周期越界:长周期持有短周期的对象引用,导致短周期对象本应该被系统回收而实际却没有。比如:静态对象持有某个 Activity 的引用,本来 Activity 在走完 onDestroy 方法后是可以被系统回收的,但因为被静态对象引用导致一直无法被系统回收。
还有诸如 Activity 被 Handle 持有而且有延时操作导致无法被回收,Handler 换为 Thread 是一样的问题。由于 Java 匿名内部类和非静态内部类会默认持有外部类的引用,所以在 Activity 的非静态内部类和匿名内部类做延时操作也会造成内存泄漏。
检测的方案主要是针对四大组件进行排查,如果匿名内部类和非静态内部类需要做延时操作要改造成静态内部类并使用弱引用来引用四大组件,同时注意单例或者静态对象不要随意持有四大组件的引用。
2.配对遗漏:Android 和 Java 中有很多内容是需要成对出现的,比如:动态广播注册和关闭注册、数据库的打开和关闭、流的打开和关闭等等,当代码遗漏了最后关闭那一步机会造成内存泄漏。
检测方案就是对所有出现需要配对的代码进行排查。
3.数据没有及时清理,比如 Map 和 List 中持有大量的数据,但是在使用完只有没有及时的清理,也会造成内存泄漏
4.Webview 造成的内存泄漏:由于 Webview 初始化的时候会消耗大量的资源,而且这些资源会一直存在进程中消耗内存。所以对于 WebView 使用很轻微的应用可以开辟一个进程来加载 Webview,在使用完之后结束掉进程,这样就会省掉很多内存的消耗,但是入过 Webview 很常用的话可以不用处理。
说的简单点,内存泄漏的原因是使用完的资源没有被释放,依旧在持续的占用内存并且不被控制。
常见的内存泄漏千千万之多,最常见也是听得最多的:
1.Bitmap对象没有调用recycle()释放内存
2.错误的使用单例(涉及到长生短命周期和强弱应用)
3.static关键字修饰的成员变量
其实都是因为对象间的引用关系处理不当,最终导致泄漏,导致我们的app卡顿、吃内存,并且crash。
内存问题的检测方式也比较多,Android Studio 的Monitors和就可以帮助我们找到大部分内存问题(内存泄漏溢出、内存抖动等等),Lint也可以帮助我们检测项目潜在问题,这样的工具还很多,大家只要愿意搜就会有一大把
内存泄露是指对象申请的内存空间得不到及时释放,累计的内存泄漏会进一步导致内存溢出。在JAVA中开发者不用主动去释放对象的内存,JVM帮我们做了这个操作。当JVM执行GC操作时,会回收已经那些已经不存活的对象。对象的存活与否可通过可达性分析算法判断,所以如果一个对象被其他生命周期长久的类持有时,则得不到及时的清理。
在android中常见的泄漏是Activity被单例或webView等持有、File、Bitmap等资源使用后没关闭回收。
在开发过程中可以使用自带的StrictMode规范代码,也借助leakCanary查看内存泄漏情况。内存泄露是指对象申请的内存空间得不到及时释放,累计的内存泄漏会进一步导致内存溢出。在JAVA中开发者不用主动去释放对象的内存,JVM帮我们做了这个操作。当JVM执行GC操作时,会回收已经那些已经不存活的对象。对象的存活与否可通过可达性分析算法判断,所以如果一个对象被其他生命周期长久的类持有时,则得不到及时的清理。
在android中常见的泄漏是Activity被单例或webView等持有、File、Bitmap等资源使用后没关闭回收。
在开发过程中可以使用自带的StrictMode规范代码,也借助leakCanary查看内存泄漏情况。面试必问知识点之一。
内存泄露的根本原因是一个长生命周期对象持有一个短生命周期对象,造成短生命周期对象没有办法被回收。因为Android系统的很多组件都涉及到生命周期,所以内存泄露问题在Android应用程序中比较常见。
常见的有:
1.Handler隐式持有Activity对象,导致Activity无法被回收;解决方案:Handler使用静态内部类,通过弱引用的方式持有Activity2.涉及一些需要释放的要在关闭的时候释放资源,比如WebView,比如IO流等3.单例不要持有Activity, Fragment, View之类有生命周期的context属性...总结(非回答):
生命周期不一致的对象存在引用情况,导致短生命对象无法回收,无法回收对象过多导致内存泄漏;
持有、短命常见场景:
非静态 内部类 持有外部类引用 导致外部类无法回收;
非静态 Handler 隐式 持有 外部Activity (条件1)
此外还有差不多意思的 AsyncTask、WebView、Thread、注册长生命周期的监听/广播、资源未及时关闭等;
Note: 它们都会持有外部activity,但是本身生命周期 与 Activity 不同步;
一些解决方式
内存泄漏:内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间。
<strike></strike>
关于内存泄漏,一般像单例模式的使用不当啊、集合的操作不当啊、资源的缺乏有效的回收机制啊、Handler、线程的使用不当等等都有可能引发内存泄漏。
1.单例模式引发的内存泄漏:原因:单例模式里的静态实例持有对象的引用,导致对象无法被回收,常见为持有Activity的引用优化:改为持有Application的引用,或者不持有使用的时候传递。2.集合操作不当引发的内存泄漏:原因:集合只增不减优化:有对应的删除或卸载操作3.线程的操作不当引发的内存泄漏:原因:线程持有对象的引用在后台执行,与对象的生命周期不一致优化:静态实例+弱引用(WeakReference)方式,使其生命周期一致4.匿名内部类/非静态内部类操作不当引发的内存泄漏:原因:内部类持有对象引用,导致无法释放,比如各种回调优化:保持生命周期一致,改为静态实例+对象的弱引用方式(WeakReference)5.常用的资源未关闭回收引发的内存泄漏:原因:BroadcastReceiver,File,Cursor,IO流,Bitmap等资源使用未关闭优化:使用后有对应的关闭和卸载机制6.Handler使用不当造成的内存泄漏:原因:Handler持有Activity的引用,其发送的Message中持有Handler的引用,当队列处理Message的时间过长会导致Handler无法被回收优化:静态实例+弱引用(WeakReference)方式内存泄露定义:就是指你想让一个对象在下次GC的时候彻底被回收,但是呢,这个对象所处的条件不符合GC所认定的应当回收的条件,而导致实际上没有被回收依然占用着内存空间,像这样的对象多了,,迟早会把内存撑爆引发大名鼎鼎的OOM问题。Android中最最露骨的就是Activity的内存泄漏。因为Activity持有了太多的东西,包括图片,数据等等吧。。它的泄露势必会浪费很大的内存空间。
内存泄露的常见场景:
1. 非静态内部类、匿名内部类
2. 静态的View3. Handler4. 监听器(各种需要注册的Listener,Watcher等)5. 资源对象没关闭造成内存泄漏6. 属性动画没移除引起的7. RxJava引起8. WebView没关闭9. 其他的系统控件以及自定义View10. 不一定非要使用弱引用才行检测方案:
1.LeakCanary.
2.Lint.Lint 是 Android studio 自带的静态代码分析工具,使用起来也很方便,选中需要扫描的 module,然后点击顶部菜单栏 Analyze -> Inspect Code ,选择需要扫描的地方即可3.StrictMode,StrictMode 是 Android 系统提供的 API,在开发环境下引入可以更早的暴露发现问题给开发者,于开发阶段解决它,StrictMode 最常被使用来检测在主线程中进行读写磁盘或者网络操作等耗时任务,把这些耗时任务放置于主线程会造成主线程阻塞卡顿甚至可能出现 ANR.
3.adb shell && Memory Usage
可以通过命令 adb shell dumpsys meminfo [package name][-d] 来将指定 package name 的内存信息打印出来,-d 选项会额外打印信息,数据单位为 KB,这种模式可以通过 Activities 选项非常直观地看到 Activity 未释放导致的内存泄漏:
4.Android Memory Profiler.Memory Profiler 是 Android Studio 自带的一个监控内存使用状态的工具
5.MAT:MAT(Memory Analyzer Tools)是一个 Eclipse 插件,它是一个快速、功能丰富的 JAVA heap 分析工具,它可以帮助我们查找内存泄漏和减少内存消耗,MAT 插件的下载地址:Eclipse Memory Analyzer Open Source Project,上面通过 Android studio 生成的 .hprof 文件因为格式稍有不同,所以我们需要现将 AS 生成的 .hprof 文件保存到本地之后,通过 hprof-conv heap-original.hprof heap-converted.hprof 命令进行转换。通过 MAT 去打开转换之后的这个文件:
内存泄漏 : 无用对象被其他对象持有,造成对象无法被系统回收。
1 : 单利导致内存泄漏
2 : 静态变量导致内存泄露
3 : 非静态内部类导致内存泄露
4 : 资源未关闭或释放
总结 :
构造单利尽量不用 activity 引用
静态引用注意对象置空
静态内部类 + 软引用代替非静态内部类
文件流,Cursor 等资源及时关闭
跟着大佬做一个回答系列:https://www.jianshu.com/p/b0345cb39819