㈠ android基础(29)内存泄漏
1)oom是什么?
2)什么情况导致oom?
3)有什么解决方法可以避免OOM?
4)孙搏Oom 是否可以try catch?为什么?
5)弯埋内存泄漏是什么?
6)什么情况导致内存泄漏?
7)如何防止线程的内存泄漏?
8)内存泄露场的解决方法
9)内存泄漏和内存溢出区别?
10)用IDE如何分析内存泄漏?
11)ANR产生的原因是什么?
12)ANR定位和修正
13)广播引起anr的时间限制是多少?
即 ML (Memory Leak)
指 程序在申请内存后,当该内存不需再使用 但 却无法被释放 & 归还给 程序的现象。
从机制上的角度来说:
出现内存泄露的原因是 无意识地持有对象引用,使得 持有引用者的生命周期 > 被引用者的生命周期
a. 内存分配策略
由 ActivityManagerService 集中管理 所有进程的内存分配
b. 内存回收策略
a. 内存分配策略
用1个实例讲解 内存分配
b. 内存则闹祥释放策略
静态成员变量有个非常典型的例子 = 单例模式
解决方案
单例模式引用的对象的生命周期 = 应用的生命周期
多线程:AsyncTask、实现Runnable接口、继承Thread类
Handler弱引用方式
㈡ 关于Android EditText导致的内存泄漏的问题
泄露信息如下:
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
Displaying only 1 leak trace out of 4 with the same signature
Signature:
┬───
│ GC Root: System class
│
├─ android.os.AsyncTask class
│ Leaking: NO (a class is never leaking)
│ ↓ static AsyncTask.SERIAL_EXECUTOR
│ ~~~~~~~~~~~~~~~
├─ android.os.AsyncTask$SerialExecutor instance
│ Leaking: UNKNOWN
│ ↓ AsyncTask$SerialExecutor.mTasks
│ ~~~~~~
├─ java.util.ArrayDeque instance
│ Leaking: UNKNOWN
│ ↓ ArrayDeque.elements
│ ~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.os.AsyncTask$SerialExecutor$1 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ AsyncTask$SerialExecutor$1.val$r
│ ~~~~~
├─ android.widget.TextView$3 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ TextView$3.this$0
│ ~~~~~~
├─ androidx.appcompat.widget.AppCompatEditText instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.input_edit
│ View.mWindowAttachCount = 1
│ mContext instance of RoomMainActivity with mDestroyed = true
│ ↓ View.mContext
╰→ RoomMainActivity instance
Leaking: YES (ObjectWatcher was watching this because RoomMainActivity
received Activity#onDestroy() callback and Activity#mDestroyed is true)
key = cb388a5c-0ff2-452f-a9b0-6ea7d03a5105
watchDurationMillis = 37631
retainedDurationMillis = 32630
mApplication instance of ChatApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
====================================
原因分析:
1、引用链结构:AsyncTask的SerialExecutor执行器引用了EditText对象,而EditText对象中的mContext引用到了RoomMainActivity中context,导致RoomMainActivity 无法销毁。
查看源码得知在TextView中有updateTextServicesLocaleAsync()方法,调用了AsyncTask.execute()向其中传入了匿名内部类Runnable,而持有了控件对象。
2、解决方法:因为在源码层面无法修改源码,在引用端切断引用链。
给EditText使用Application的上下文,在EditText使用的页面退出销毁时移除EditText控件,包括置空它的监听器、清除它的焦点。
import android.content.Context;
import android.os.Build;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ViewGroup;
import androidx.appcompat.widget.AppCompatEditText;
/**
* 关于Android EditText导致的内存泄漏的问题
*/
public class NoMemoryLeakEditTextextends AppCompatEditText {
public NoMemoryLeakEditText(Context context) {
super(context.getApplicationContext());
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
public void clearMemoryLeak(TextWatcher watcher, ViewGroup container) {
clearFocus();
setOnTouchListener(null);
setOnClickListener(null);
setOnDragListener(null);
setOnKeyListener(null);
setOnLongClickListener(null);
setOnEditorActionListener(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
(null);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnScrollChangeListener(null);
}
setOnFocusChangeListener(null);
removeTextChangedListener(watcher);
container.removeView(this);
}
}
最后在页面销毁的地方调用clearMemoryLeak方法
㈢ Android中什么情况下会导致内存泄露
集合类泄漏
集合类如果仅仅有添加元素的方法,而没有相应的删除机制,导致内存被占用。如果这个集合类是全局性的变量 (比如类中的静态属性,全局性的 map 等即有静态引用或 final 一直指向它),那么没有相应的删除机制,很可能导致集合所占用的内存只增不减。比如上面的典型例子就是其中一种情况,当然实际上我们在项目中肯定不会写这么 2B 的代码,但稍不注意还是很容易出现这种情况,比如我们都喜欢通过 HashMap 做一些缓存之类的事,这种情况就要多留一些心眼。
单例造成的内存泄漏
由于单例的静态特性使得其生命周期跟应用的生命周期一样长,所以如果使用不恰当的话,很容易造成内存泄漏
匿名内部类/非静态内部类和异步线程
非静态内部类创建静态实例造成的内存泄漏
有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源
Handler 造成的内存泄漏
Handler 的使用造成的内存泄漏问题应该说是最为常见了,很多时候我们为了避免 ANR 而不在主线程进行耗时操作,在处理网络任务或者封装一些请求回调等api都借助Handler来处理,但 Handler 不是万能的,对于 Handler 的使用代码编写一不规范即有可能造成内存泄漏。另外,我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有。
由于 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易导致无法正确释放。
㈣ Android线程泄漏场景以及解决办法
1.非静态内部类的静态实例
非静态内部类会持有外部类的引用,如果非静态内部类的实例是静态的,就会长期的维持着外部类的引用,组织被系统回收,解决办法是使用静态内部类
2.多线程相关的匿名内部类和非静态内部类
匿名内部类同样会持有外部类的引用,如果在线程中执行耗时操作就有可能发生内存泄漏,导致外部类无法被回收,直到耗时任务结束,解决办法是在页面退出时结束线程中的任务
3.Handler内存泄漏
Handler导致的内存泄漏也可以被归纳为非静态内部类导致的,Handler内部message是被存储在MessageQueue中的,有些message不能马上被处理,存在的时间会很长,导致handler无法被回收,如果handler是非静态的,就会导致它的外部类无法被回收,解决办法是1.使用静态handler,外部类引用使用弱引用处理2.在退出页面时移除消息队列中的消息
4.Context导致内存泄漏
根据场景确定使用Activity的Context还是Application的Context,因为二者生命周期不同,对于不必须使用Activity的Context的场景(Dialog),一律采用Application的Context,单例模式是最常见的发生此泄漏的场景,比如传入一个Activity的Context被静态类引用,导致无法回收
5.静态View导致泄漏
使用静态View可以避免每次启动Activity都去读取并渲染View,但是静态View会持有Activity的引用,导致无法回收,解决办法是在Activity销毁的时候将静态View设置为null(View一旦被加载到界面中将会持有一个Context对象的引用,在这个例子中,这个context对象是我们的Activity,声明一个静态变量引用这个View,也就引用了activity)
6.WebView导致的内存泄漏
WebView只要使用一次,内存就不会被释放,所以WebView都存在内存泄漏的问题,通常的解决办法是为WebView单开一个进程,使用AIDL进行通信,根据业务需求在合适的时机释放掉
7.资源对象未关闭导致
如Cursor,File,Broadcast等,内部往往都使用了缓冲,会造成内存泄漏,一定要确保关闭它并将引用置为null
8.集合中的对象未清理
集合用扮行团于保存对象,如果集合越来越大,不进行合理的清理,尤其是入股集合是静态的
9.Bitmap导致内存泄漏
bitmap是比较占内存的,所以一定要在不使用的时候及时进行清理,避免静态变量持有大的bitmap对象
10.监听器未关闭
很多需要register和unregister的系统服务要在合适的时候进行unregister,手动添加的listener也厅橘需要带颤及时移除
㈤ 使用ConnectivityManager的内存泄漏隐患
Android里面内存泄漏问题最突出的就是Activity的泄漏,而泄漏的根源大多在于单例的使用,也就是一个静态实例持有了Activity的引用。静态变量的生命周期与应用(Application)是相同的,而键启Activity生命周期通常比它短,也就会造成在Activity生命周期结束后,还被引用导致无法被系统回收释放。
生成静态引用内存泄漏可能有两种情况:
这个主要讲下系统级的情况,这样的情况可能也有很多,举个最近发现的问题ConnectivityManager。
通常我们获取系统服务时采用如下方式:
在Android6.0系统上,如果这里的Context如果是Activity的实例,那么即使你什么也不干也会造成内存泄漏。
用LeakCanary可以直接看到内存泄漏:
一步一步来分析下。
先从Context的getSystemService方法开始,我们知道Activity是从ContextWrapper继承而来的,ContextWrapper中持有一个mBase实例,这个实例指向一个ContextImpl对象,同时ContextImpl对象持有一个OuterContext对象,对于Activity来说,这个OuterContext就是Activity对象。所以调用getSystemService最终会调用到ContextImpl的getSystemService方法。
在6.0上 ContextImpl 的getSystemService方法调用 SystemServiceRegistry 来完成。
SystemServiceRegistry提供ConnectivityManager的实例。
在6.0上, ConnectivityManager 实现为单例:
精彩的部分来了,ConnectivityManager 持庆亮贺有了一个Context的引用:
这个Context在ConnectivityManager 创建时传入,这个Context在中由ContextImpl对象转换为OuterContext,与就是Activity对象,所以最终ConnectivityManager的单实例持有了Activity的实例引用。这样即使Activity退出后仍然无法释放,导致誉派内存泄漏。
这个问题仅在6.0上出现,在5.1上ConnectivityManager实现为单例但不持有Context的引用,在5.0有以下版本ConnectivityManager既不为单例,也不持有Context的引用。
其他服务没认真研究,不确定有没有这个问题。不过为了避免类似的情况发生,最好的解决办法就是:
㈥ Android技术分享|Android 中部分内存泄漏示例及解决方案
内存泄漏:
举例:
请注意以下的例子是虚构的
内存抖动
源自Android文档中的 Memory churn 一词,中文翻译为内存抖动。
指快速频繁的创建对象从而产生的性能问题。
引用Android文档原文:
Java内存泄漏的根本原因是 长生命周期 的对象持有 短生命周期 对象的引用就很可能发生内存泄漏。
尽管短生命周期对象已经不再需要,但因为长生命周期依旧持有它的引用,故不能被回收而导致内存泄漏。
静态集合类引起的内存泄漏
如果仅仅释放引用本身(tO = null), ArrayList 依然在引用该对象,GC无法回收。
监听器
在Java应用中,通常会用到很多监听器,一般通过 addXXXXListener() 实现。但释放对象时通常会忘记删除监听器,从而增加内存泄漏的风险。
各种连接
如数据库连接、网络连接(Socket)和I/O连接。忘记显式调用 close() 方法引起的内存泄漏。
内部类和外部模块的引用
内部类的引用是很容易被遗忘的一种,一旦没有释放可能会导致一系列后续对象无法释放。此外还要小心外部模块不经意的引用,内部类是否提供相应的操作去除外部引用。
单例模式
由于单例的静态特性,使其生命周期与应用的生命周期一样长,一旦使用不恰当极易造成内存泄漏。如果单利持有外部引用,需要注意提供释放方式,否则当外部对象无法被正常回收时,会进而导致内存泄漏。
集合类泄漏
如集合的使用范围超过逻辑代码的范围,需要格外注意删除机制是否完善可靠。比如由静态属性 static 指向的集合。
单利泄漏
以下为简单逻辑代码,只为举例说明内存泄漏问题,不保证单利模式的可靠性。
AppManager 创建时需要传入一个 Context ,这个 Context 的生命周期长短至关重要。
1. 如果传入的是 Application 的 Context ,因为 Application 的生命周期等同于应用的生命周期,所以没有任何问题。
2. 如果传入的是 Activity 的 Context ,则需要考虑这个 Activity 是否在整个生命周期都不会被回收了,如果不是,则会造成内存泄漏。
非静态内部类创建静态实例造成的内存泄漏
应该将该内部类单独封装为一个单例来使用。
匿名内部类/异步线程
Runnable都使用了匿名内部类,将持有MyActivity的引用。如果任务在Activity销毁前未完成,将导致Activity的内存无法被回收,从而造成内存泄漏。
解决方法:将Runnable独立出来或使用静态内部类,可以避免因持有外部对象导致的内存泄漏。
Handler造成的内存泄漏
Handler属于TLS(Thread Local Storage)变量,生命周期与Activity是不一致的,容易导致持有的对象无法正确被释放
当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。
当主线程中实例化一个Handler对象后,它就会自动与主线程Looper的MessageQueue关联起来。所有发送到MessageQueue的Messag都会持有Handler的引用,所以Looper会据此回调Handle的handleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。
另外,主线程的Looper对象会伴随该应用程序的整个生命周期。
在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。
当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用,在这里就是指 SampleActivity)。
避免不必要的静态成员变量
对于BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等资源的使用,应在Activity销毁前及时关闭或注销。
不使用WebView对象时,应调用`destroy()`方法销毁。
㈦ android内存泄漏怎么解决
其实在Android中会造成内存泄露则首迹的情景无外乎两种:
全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。
活在Activity生命周期之外的线程。没有清空对Activity的强引用。
检查一下你的项目中芹洞是否有以下几种情况孙并:
Static Activities
Static Views
Inner Classes
Anonymous Classes
Handler
Threads
TimerTask
Sensor Manager
对应加以解决。
㈧ Android常见内存泄漏
如果 thread 为静态变量,即使在 onDestroy 方法中,主动置空 thread 变量,依然会有内存泄漏。因为此时泄漏包含了 静态变量 和 子线程为非静态内部类实例 两种情况,他们都持有 Activity ,且生命周期比 Activity 更长,线程相关的下盯毁液面三种泄露余余情况也是一样的。
子线程在 SingleTest 单例中:
注意:
单例:
Activity:
单例:
Activity::
还有一种情况,把 registerReceiver(mReceiver, intentFilter) 改成 getApplication().getApplicationContext().registerReceiver(mReceiver, intentFilter) ,即 Application 代替 Activity 注册广播,如果 MyReceiver 和 MyFilter 也都是静态类,不取消注册不会导致 Activity 泄露,因为使用的是 Application 的上下文。但如果它哥俩有一个是内部类或者匿名类,也等于 AMS 间接持有 Activity ,则仍会引发 Activity 泄露。最好还是注册广播后,在销毁时主动取消注册。
及时 close 资源。
调用 bitmap.recycle() 回收。
及时清空集合中的对象。凯物