写点什么

关于内存泄漏和内存溢出的那些事!

用户头像
Android架构
关注
发布于: 1 小时前

public static AppManager getInstance(Context context) {


if (instance != null) {


instance = new AppManager(context);


}


return instance;


}


}


这样不管传入什么 Context 最终将使用 Application 的 Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏。???


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 时都会使用该单例的数据。虽然这样避免了资源的重复创建,但是这种写法却会造成内存泄漏。因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该 Activity 的引用,从而导致 Activity 的内存资源不能被正常回收。 解决方法:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用 Context,就使用 Application 的 Context。


3、Handler 造成的内存泄漏


示例:创建匿名内部类的静态对象


public class MainActivity extends AppCompatActivity {


private final Handler handler = new Handler() {


@Override


public void handleMessage(Message msg) {


// ...


}


};


@Overr


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ide


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 中有未处理的 Message,Looper 就会不断的从中取出并交给 Handler 处理。另外,主线程的 Looper 对象会伴随该应用程序的整个生命周期。


2、 Java 角度 在 Java 中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。


对上述的示例进行分析,当 MainActivity 结束时,未处理的消息持有 handler 的引用,而 handler 又持有它所属的外部类也就是 MainActivity 的引用。这条引用关系会一直保持直到消息得到处理,这样阻止了 MainActivity 被垃圾回收器回收,从而造成了内存泄漏。 解决方法:将 Handler 类独立出来或者使用静态内部类,这样便可以避免内存泄漏。


3、线程造成的内存泄漏


示例: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<Void, Void, Void> {


// ...


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)比如在 Activity 中 register 了一个 BraodcastReceiver,但在 Activity 结束后没有 unregister 该 BraodcastReceiver。


2)资源性对象比如 Cursor,Stream、File 文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java 虚拟机内,还存在于 java 虚拟机外。如果我们仅仅是把它的引用设置为 null,而不关闭它们,往往会造成内存泄漏。


3)对于资源性对象在不使用的时候,应该调用它的 close()函数将其关闭掉,然后再设置为 null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。


4)Bitmap 对象不在使用时调用 recycle()释放内存。2.3 以后的 bitmap 应该是不需要手动 recycle 了,内存已经在 java 层了。


6、使用 ListView 时造成的内存泄漏


初始时 ListView 会从 BaseAdapter 中根据当前的屏幕布局实例化一定数量的 View 对象,同时 ListView 会将这些 View 对象缓存起来。当向上滚动 ListView 时,原先位于最上面的 Item 的 View 对象会被回收,然后被用来构造新出现在下面的 Item。这个构造过程就是由 getView()方法完成的,getView()的第二个形参 convertView 就是被缓存起来的 Item 的 View 对象(初始化时缓存中没有 View 对象则 convertView 是 null)。


构造 Adapter 时,没有使用缓存的 convertView。 解决方法:在构造 Adapter 时,使用缓存的 convertView。


7、集合容器中的内存泄露


我们通常把一些对象的引用加入到了集合容器(比如 ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是 static 的话,那情况就更严重了。 解决方法:在退出程序之前,将集合里的东西 clear,然后置为 null,再退出程序。 8、WebView 造成的泄露


当我们不要使用 WebView 对象时,应该调用它的 destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。 解决方法:为 WebView 另外开启一个进程,通过 AIDL 与主线程进行通信,WebView 所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。


3、如何检测内存泄漏


============================================================================


内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内 bai 存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。 可以使用相应的软件测试工具对软件进行检测。


  1. ccmalloc-Linux 和 Solaris 下对 C 和 C++程序的简单的使用内存泄漏和 malloc 调试库。

  2. Dmalloc-Debug Malloc Library.

  3. Electric


Fence-Linux 分发版中由 Bruce Perens 编写的 malloc()调试库。


  1. Leaky-Linux 下检测内存泄漏的程序。

  2. LeakTracer-Linux、Solaris 和 HP-UX 下跟踪和分析 C++程序中的内存泄漏。

  3. MEMWATCH-由 Johan


Lindh 编写,是一个开放源代码 C 语言内存错误检测工具,主要是通过 gcc 的 precessor 来进行。


  1. Valgrind-Debugging and profiling Linux programs, aiming at


programs written in C and C++.


  1. KCachegrind-A visualization tool for the profiling data


generated by Cachegrind and Calltree.


  1. Leak


Monitor-一个 Firefox 扩展,能找出跟 Firefox 相关的泄漏类型。


  1. IE Leak Detector


(Drip/IE Sieve)-Drip 和 IE Sieve leak detectors 帮助网页开发员提升动态网页性能通过报告可避免的因为 IE 局限的内存泄漏。


  1. Windows Leaks


Detector-探测任何 Win32 应用程序中的任何资源泄漏(内存,句柄等),基于 Win API 调用钩子。


  1. SAP Memory


Analyzer-是一款开源的 JAVA 内存分析软件,可用于辅助查找 JAVA 程序的内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于 Eclipse RCP(Rich Client Platform),可以下载 RCP 的独立版本或者 Eclipse 的插件。


  1. DTrace-即动态跟踪 Dynamic


Tracing,是一款开源软件,能在 Unix 类似平台运行,用户能够动态检测操作系统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。


  1. IBM Rational PurifyPlus-帮助开发人员查明 C/C++、托管.NET、Java 和 VB6 代码中的性能和可靠性错误。PurifyPlus


将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。


  1. Parasoft Insure++-针对 C/C++应用的运行时错误自动检测工具,它能够自动监测 C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和 I/O 等错误。并通过使用一系列独特的技术(SCI 技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为 Microsoft


Visual C++的一个插件运行。


  1. Compuware DevPartner for Visual C++ BoundsChecker


Suite-为 C++开发者设计的运行错误检测和调试工具软件。作为 Microsoft Visual Studio 和 C++ 6.0 的一个插件运行。


  1. Electric Software GlowCode-包括内存泄漏检查,code


profiler,函数调用跟踪等功能。给 C++和.Net 开发者提供完整的错误诊断,和运行时性能分析工具包。


  1. Compuware DevPartner Java


Edition-包含 Java 内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。


  1. Quest JProbe-分析 Java 的内存泄漏。

  2. ej-technologies JProfiler-一个全功能的 Java 剖析工具,专用于分析 J2SE 和 J2EE 应用程序。它把 CPU、执行绪和内存的剖析组合在一个强大的应用中。JProfiler 可提供许多 IDE 整合和应用服务器整合用途。JProfiler 直觉式的 GUI 让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。4.3.2 注册码:A-G666#76114F-1olm9mv1i5uuly#0126

  3. BEA JRockit-用来诊断 Java 内存泄漏并指出根本原因,专门针对 Intel 平台并得到优化,能在 Intel 硬件上获得最高的性能。

  4. SciTech Software AB .NET Memory


Profiler-找到内存泄漏并优化内存使用针对 C#,VB.Net,或其它.Net 程序。


  1. YourKit .NET & Java Profiler-业界领先的 Java 和.NET 程序性能分析工具。

  2. AutomatedQA AQTime-AutomatedQA 的获奖产品 performance profiling 和 memory


debugging 工具集的下一代替换产品,支持 Microsoft, Borland, Intel, Compaq 和 GNU 编译器。可以为.NET 和 Windows 程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0 和 Windows 32/64 位应用程序。

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
关于内存泄漏和内存溢出的那些事!