android 内存泄漏

来源:互联网 发布:手机淘宝怎样撤销投诉 编辑:程序博客网 时间:2024/04/30 17:39

参考: http://blog.csdn.net/gemmem/article/details/13017999
http://blog.csdn.net/aaa1117a8w5s6d/article/details/8251456
首先,要明白java内存管理机制:
静态存储区:(方法区):主要存放静态数据,全局static数据和常量。这块内存在程序编译时已经分配好,并且在程序整个运行期间都存在。
栈区: 当方法被执行时,方法体内的局部变量都在栈上创建,并且在方法执行结束时局部变量所持有的内存将会自动被释放
堆区:(动态内存分配)程序运行时直接new出来的对象所占内存,这部分内存在不使用时会由java垃圾回收器负责回收。
java内存管理就是对象的分配和释放问题

在函数(代码块)中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量所分配的内存空间;在堆中分配的内存由java虚拟机的自动垃圾回收器来管理
堆和栈的优缺点:
堆的优势是可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的。
缺点就是要在运行时动态分配内存,存取速度较慢;栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。
另外,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是他们却可以直接或间接的引用gc roots导致无法被GC回收,无用的对象占据着内存控件,使得实际可使用内存变小,形象的说法就是内存泄漏。

查找方法
1.压力测试adb命令查看内存变化
2.MAT分析
3.LeakCanary检测

可用android studio的AndroidMonitor获取内存片段,InitalGC回收内存,DumpJavaHeap获取hprof-conv文文件
常见导致内存泄漏的场景

非静态内部类的静态实例容易造成内存泄漏

public class MainActivityextends Activity  {           static Demo sInstance = null;      @Override      public void onCreate(BundlesavedInstanceState)      {          super.onCreate(savedInstanceState);          setContentView(R.layout.activity_main);          if (sInstance == null)          {             sInstance= new Demo();          }      }      class Demo      {      voiddoSomething()      {                 System.out.print("dosth.");      }      }  } 

上面的代码中的sInstance实例类型为静态实例,在第一个MainActivity act1实例创建时,sInstance会获得并一直持有act1的引用。当MainAcitivity销毁后重建,因为sInstance持有act1的引用,所以act1是无法被GC回收的,进程中会存在2个MainActivity实例(act1和重建后的MainActivity实例),这个act1对象就是一个无用的但一直占用内存的对象,即无法回收的垃圾对象。所以,对于lauchMode不是singleInstance的Activity, 应该避免在activity里面实例化其非静态内部类的静态实例。

2.activity使用静态成员

private static Drawable sBackground;    @Override    protected void onCreate(Bundle state) {        super.onCreate(state);        TextView label = new TextView(this);        label.setText("Leaks are bad");        if (sBackground == null) {            sBackground = getDrawable(R.drawable.large_bitmap);        }        label.setBackgroundDrawable(sBackground);        setContentView(label);    }  

静态成员缓存了drawable对象,activity加载速度回加快,这样做事错误的,因为在android2.3系统上,他会导致activity销毁后无法被系统回收。
label .setBackgroundDrawable函数调用会将label赋值给sBackground的成员变量mCallback。

上面代码意味着:sBackground(GC Root)会持有TextView对象,而TextView持有Activity对象。所以导致Activity对象无法被系统回收。
3.handler内存泄漏问题
我们知道,Handler通过发送Message与其他线程交互,Message发出之后是存储在目标线程的MessageQueue中的,而有时候Message也不是马上就被处理的,可能会驻留比较久的时间。在Message类中存在一个成员变量 target,它强引用了handler实例,如果Message在Queue中一直存在,就会导致handler实例无法被回收,如果handler对应的类是非静态内部类 ,则会导致外部类实例(Activity或者Service)不会被回收,这就造成了外部类实例的泄露。 所以正确处理Handler等之类的内部类,应该将自己的Handler定义为静态内部类,并且在类中增加一个成员变量,用来弱引用外部类实例,如下:
这里写图片描述

4.注册某个对象后未反注册
假设我们在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度)则可以在LockScreen中定义个phoneStateListener对象,同时将它注册到TelephonyManager服务中,对于LockScreen对象,当需要显示锁屏界面的时候,就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen会被释放掉。
但是如果在释放LockScreen对象的时候,忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被GC回收,如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法回收而引起OOM,使进程崩溃。
5集合中对象没清理造成的内存泄漏
我们通常会把一些对象的引用加入到集合中,当我们不需要改对象时,如果没有把它的引用从集合中清理掉,集合就会越来越大,这个集合是static的 话,情况就更严重了。
6资源对象没关闭造成内存泄露
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于Java虚拟机内,还存在于Java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该立即调用它的close()函数,将其关闭掉,然后再置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

  程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在长时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险

8一些不良代码造成内存压力

1.handler延时发送的消息在界面销毁的时候没有移除
* 2.注册的BroadReceiver在界面销毁的时候没有取消注册*
3.操作db打开的Cursor对象没有关闭
4.自己创建的大对象比如Bitmap,在用完没有释放
5.IO操作的时候及时关闭流对象

handler内存泄漏原因和解决方法
http://www.linuxidc.com/Linux/2013-12/94065.htm

0 0
原创粉丝点击