Android-内存泄露

2019-05-23  本文已影响0人  上山走18398

<application android:largelHeap="true" 可以申请大内存,但是gc时间也会加长

主体为内存,什么是内存,内存是如何分配和管理,安卓系统对内存的处理,会为每个程序分配内存,超过了就会被系统干掉
内存溢出和内存泄露的区别:
内存溢出:没有可分配的内存了
内存泄露:申请的内存没有及时释放

Java中的内存分配
  1. 静态存储区:编译时就分配好,在程序整个运行期间都存在。它主要存放静态数据和常量
  2. 栈区:当方法执行时,会在栈区内存中创建方法体内部的局部变量,方法结束后自动释放
  3. 堆区:通常存放new出来的对象。由java垃圾回收器回收
四种引用类型
1. 强引用 : 只要强引用存在,垃圾回收器永远不会回收被引用的对象,哪怕内存不足时,JVM抛出OutOfMemoryError
            a. 可以显示的将强引用置为null
            b. 在方法内部创建的对象,由于强引用保存在栈上,所引用的对象保存在堆空间中,方法运行完成后就会推出方法栈,从而让强引用断裂

2. 软引用:描述一些非必须但仍有用的对象,只有内存不足时,才会回收这些软引用对象,如果回收后仍没有内存,则抛出内存溢出的异常
    这种特性常被用来实现高速缓存技术,,如网页缓存,图片缓存
    
例如对于已经加载过的网页,如果是通过强引用实现的,要么主动赋值为null(强引用断裂---这个就需要重新加载了),要没什么也不做(大量加载过的网页停留在内存中,最终造成outofMemory)

3. 弱引用 

4. 虚引用
内存泄露常见问题定位
  1. Handler会造成内存泄露
    a. 当一个app被启动时,android会帮我们创建一个供ui线程使用的消息队列Looper-用来处理ui线程上的事件(一条一条处理)
    b. message对象有个target,所谓?? target就是一个handler ?? ,定义是一个内部类构造对象
    c. 这个内部类构造的对象持有了外部类Activity的引用!所以导致activity无法真正的释放掉
    把内部类改成静态类 ->有可能就无法调用activity的非静态属性 ->改成弱引用,
    虚拟机在第一次gc的时候会把没有任何引用对象和只有弱引用的对象全部回收

不要让生命周期长于Activity的对象持有到Activity的对象
垃圾回收不能解决内存泄露

长生命周期的对象持有短生命周期对象引用就很有可能发生内存泄露

Activity Context的不正确使用
使用ApplicationContext代替ActivityContext,因为ApplicationContext会随着应用程序的存在而存在,而不依赖activity的生命周期

  1. 对Context的引用不要超过它本身的生命周期,慎重的对Context使用“static”关键字。Context里如果有线程,一定要在OnDestroy()中及时停掉
  1. 内存抖动

内存泄露发生时的主要表现为内存抖动,可用内存慢慢表少

  1. 大内存对象被分配

  2. 频繁GC

安卓常见内存泄露

a. 单例造成 --生命周期和应用生命周期一样长,如果一个对象(如Context)已经不再使用,而单例对象还持有对象的引用造成这个对象不被回收
b. 非静态内部类创建静态实例造成 :
在activity内存创建一个非静态内部类单例,避免每次启动资源重新创建。
c. handler造成:子线程执行网络任务,使用Handler处理子线程发送信息 ,activity退出还有未处理或者正在处理的消息时,消息队列中的消息持有handler对象引用,handler又持有Activity,导致Activity的内存和资源不能及时被回收
d. 线程造成:匿名内部类Runnable 和AsyncTask 对象执行异步任务,对当前Activity隐式引用,在Activity销毁之前,任务还没有执行完,将导致Activity的内存和资源不能及时回收
e. 资源未关闭造成的内存泄露 : 当Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄露
f. 界面切换,多次横竖屏造成OOM

二 内存数据获取

  1. 各种Linux命令 (top free meminfo)
  2. dumpsys
  3. 工具
内存泄露常见原因

长生命周期的对象引用短生命周期的对象,导致短生命周期的引用不被回收

  1. 静态集合类引起内存泄露

静态变量的生命周期和应用程序一致,他们所引用的Object对象也不被释放

  1. 当集合里面的对象属性被修改后,再调用remove()方法时不起作用。

修改元素属性时,导致hashcode值发生改变

  1. 监听器

释放对象时,没有删除这些监听器,导致内存泄露的风险
系统服务可用通过Context.getSystemService获取,他们负责执行某些后台任务,或者为硬件访问提供接口

  1. 各种连接

数据库连接
文件读写
网络连接(socket)
io连接

  1. 内部类和外部模块的引用

内部类的引用时是比较容易遗忘的一块,而且一旦没释放可能导致一系列的后继对象没有释放
程序员A负责A模块,调用了B模块的一个方法,传入一个对象,很可能模块B保持了对该对象的引用,这时候就需要去除该对象的引用
非静态内部类和匿名类都会潜在的引用它们所属的外部类,但是,静态类却不会
如果这个非静态内部类实例做了一些耗时操作,就会造成外围对象不会被回收,从而导致内存泄露
解决方案:
将内部类变成静态内部类(静态无法引用非静态)
1. 如果强引用Activity中的属性,则将该属性引用方式改为弱引用
2. 在业务允许的情况下,当Activity执行onDestory时,结束这些耗时任务

  1. 单例模式

不正确的使用单例模式是引起内存泄露的一个常见问题
单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式)
如果单例对象持有外部对象的引用,那么这个外部对象将不能被jvm正常回收,导致内存泄露
解决方案:将该属性的引用方式改为弱引用(weakReference)
如果传入Context,使用ApplicationContext

三 常用工具

MAT(Memory Analyzer Tools) Eclipse的一个插件,java heap分析工具,可用帮助我们查找内存泄露和减少内存消耗
www.cnblogs.com/larack/p/6071209.html

  1. 观察heap: heap size堆的大小,可分配
    DDMS可以将当前内存dump成一个hprof格式的文件,MAT 读取这个文件方便阅读
  2. Histogram 查询
    2.1 它按类名将所有的实例对象列出来,可以点击表头进行排序,在表的第一行可以输入正则表达式来匹配结果
    2.2 with incoming refs ---展示了对象间的引用关系
    2.3 快速找出某个实例没被释放的原因,---> exclude all phantom/weak/soft etc. refrence
    2.4 对比两个dump结果

Android Profiler

参考链接:
https://zhuanlan.zhihu.com/p/25213586

上一篇下一篇

猜你喜欢

热点阅读