
(内存泄漏,内存泄漏)
为什么会有内存泄漏?
当不再需要使用某个对象并应对其进行回收时,正在使用的另一个对象将保留其引用,以便无法对其进行回收,这导致本应回收的对象无法被回收并留在堆中,这会导致内存泄漏。
内存泄漏对程序的影响?
内存泄漏是应用程序中OOM的主要原因之一。我们知道,Android系统为每个应用程序分配的内存是有限的,并且当应用程序中有更多的内存泄漏时,这将不可避免地导致应用程序所需的内存超过系统分配的内存配额,从而导致内存不足溢出会导致应用程序崩溃。
如何检查和分析内存泄漏?
由于内存泄漏在堆内存中,因此我们看不到它。通常我们可以使用MAT,LeakCanary等工具来检测应用程序是否存在内存泄漏。
1、MAT是一种功能强大且具有许多复杂功能的内存分析工具。
2、LeakCanary是由Square开源的轻量级第三方内存泄漏检测工具。当在程序中检测到内存泄漏时,它将以最直观的方式告诉我们发生内存泄漏的位置。谁造成了泄漏,无法回收。
常见内存泄漏和解决方法1、由单例导致的内存泄漏
由于单例的静态特性,其生命周期与应用程序的生命周期一样长。如果不再需要某个对象,而单例对象仍保留对该对象的引用,则该对象将无法进行“正常”恢复,从而导致内存泄漏。
示例:防止单例导致内存泄漏。

// 使用了单例模式
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
通过这种方式,无论传入什么上下文,最终都将使用应用程序上下文,并且单例的生命周期与应用程序一样长,这可以防止内存泄漏。 ? ? ?
2、由于创建非静态内部类的静态实例而导致的内存泄漏
例如,有时我们可能会频繁地在Activity中开始,为避免重复创建相同的数据资源,可能会显示以下内容:
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mResource == null){
mResource = new TestResource();
}
//...
}
class TestResource {
//...
}
}
通过这种方式,在Activity内部创建了一个非静态内部类的单例,并且每次Activity启动时都会使用该单例的数据。尽管这样可以避免重复创建资源,但是这种写入方式将导致内存泄漏。因为默认情况下非静态内部类将保存外部类的引用,并且非静态内部类创建静态实例,所以实例的生命周期与应用程序一样长,这导致静态实例始终保持活动的引用,这将导致活动的内存资源无法正常恢复。
解决方案:将内部类设置为静态内部类,或提取内部类并将其封装为单例。如果需要使用上下文,请使用应用程序上下文。
3、处理程序引起的内存泄漏
示例:创建匿名内部类的静态对象
public class MainActivity extends AppCompatActivity {
private final Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
// ...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
// ...
handler.sendEmptyMessage(0x123);
}
});
}
}
1、从Android的角度来看
启动Android应用程序时,该应用程序的主线程将自动创建Looper对象及其关联的MessageQueue。在主线程中实例化Handler对象时,该对象将自动与主线程Looper的MessageQueue关联。发送到MessageQueue的所有Messag都将保留对Handler的引用,因此Looper将相应地回调Handle的handleMessage()方法以处理消息。只要MessageQueue中有未处理的消息,Looper就会继续将它们取出并交给处理程序进行处理。另外,主线程的Looper对象将伴随应用程序的整个生命周期。

2、Java观点
在Java中,非静态内部类和匿名内部类都可能持有对它们所属的外部类的引用,但是静态内部类则不然。
分析上面的示例,当MainActivity结束时,未处理的消息保存对处理程序的引用,而处理程序保存对它所属的外部类MainActivity的引用。该引用关系将一直保持到消息被处理为止,这将防止MainActivity被垃圾回收器回收,从而导致内存泄漏。
解决方案:分开Handler类或使用静态内部类以避免内存泄漏。
4、线程引起的内存泄漏
示例:AsyncTask和Runnable
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
}
class MyAsyncTask extends AsyncTask {
// ...
public MyAsyncTask(Context context) {
// ...
}
@Override
protected Void doInBackground(Void... params) {
// ...
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
// ...
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
// ...
}
}
}
AsyncTask和Runnable都使用匿名内部类,因此它们将持有对它们所在的Activity的隐式引用。如果在销毁Activity之前任务未完成,则不会回收Activity的内存资源,从而导致在内存泄漏中。
解决方案:将AsyncTask和Runnable类分开,或者使用静态内部类以避免内存泄漏。
5、由于资源未封闭导致内存泄漏
对于BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应在Activity被销毁时及时关闭或注销,否则这些资源将不会被回收,从而导致内存泄漏。
1)例如,在活动中注册了BraodcastReceiver,但活动结束后并未注销BraodcastReceiver。

2)游标,流,文件文件等资源对象经常使用某些缓冲区。不使用它们时,应及时关闭它们,以便它们的缓冲区可以及时回收内存。它们的缓冲区不仅存在于Java虚拟机中,而且还存在于Java虚拟机之外。如果我们仅将其引用设置为null而不关闭它们,则通常会导致内存泄漏。
3)当不使用资源对象时,应调用其close()函数以将其关闭,然后将其设置为null。当程序退出时,我们必须确保资源对象已关闭。
4)当不使用位图对象时,调用recycle()释放内存。2.3之后的位图不需要手动回收,内存已经在java层中。
6、使用ListView时发生内存泄漏
首先,ListView将根据当前屏幕布局从BaseAdapter实例化一定数量的View对象,并且ListView将缓存这些View对象。当ListView向上滚动时,最初位于顶部的Item的View对象将被回收,然后用于构造在下面新显示的Item。此构造过程由getView()方法完成。 getView()的第二个参数convertView是要缓存的Item的View对象(初始化期间如果缓存中没有View对象,则convertView为null)。
构造适配器时,不使用缓存的convertView。
解决方案:构造适配器时,请使用缓存的convertView。
7、收集容器中的内存泄漏
我们通常将某些对象的引用添加到集合容器(例如ArrayList)。当我们不需要对象时,我们不会从集合中清除其引用,因此集合将变得越来越大。如果此集合是静态的,情况将更加严重。
解决方案:退出程序之前,清除集合中的项目,然后将其设置为null,然后退出程序。
8、WebView引起的泄漏
当我们不使用WebView对象时,应该调用它的destroy()函数来销毁它并释放它所占用的内存,否则它长时间占用的内存将无法回收,从而导致内存泄漏。

解决方案:为WebView打开另一个进程,通过AIDL与主线程进行通信,可以根据业务需要在适当的时候销毁WebView所在的进程,以实现内存的完全释放。
如何避免内存泄漏?
1、在使用上下文时,应将“应用程序上下文”用于生命周期比“活动”更长的对象。在使用上下文优先于应用程序上下文的情况下,它当然不是灵丹妙药。对于某些地方,必须使用活动上下文。应用,服务和活动上下文的应用场景如下:

其中,NO1表示应用程序和服务可以启动活动,但是需要创建新的任务任务队列。对于对话框,只能在活动中创建。这三个都可以使用。
2、由于需要在静态内部类中使用非静态外部成员变量(例如:Context,View),因此可以在静态内部类中使用弱引用来引用外部类变量,以避免内存泄漏。
3、对于不再需要的对象,将其显式分配为null,例如在使用Bitmap之后调用recycle(),然后将其分配为null。
4、对对象的生命周期保持敏感,特别注意单例,静态对象,全局集合等的生命周期。
5、对于生命周期比“活动”更长的内部类对象,在内部类中使用外部类的成员变量,可以这样做以避免内存泄漏:
1)将内部类更改为静态内部类
2)在静态内部类中使用弱引用来引用外部类的成员变量
参考
Android内存泄漏摘要
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-330340-1.html
老个JJ新闻说啥你就以为啥
自始至终都是日舰比北洋多
>我自愿为国家尊严而战