内存泄漏整理

java优化

  • 预知容量的情况下构造ArrayList时尽量指定初始大小(ArrayList内部的扩容策略是当其所存储的元素数量超过它已有的大小时,它就会以1.5倍的容量进行扩容而Vector内部扩容策略为按需扩容,每次+1。同样,在众多Map集合中也有各自扩容策略,比如HashMap每次扩容时新容量等于原始的容量*2。在我们常用做字符串拼接的StringBuffer和StringBuilder内部,实际上也是有扩容策略,默认为扩容为原始的1.5倍。)
  • 如果一个方法不需要使用该对象的成员,那么把该方法设为static(静态调用该方法比对象调用该方法快15%~20%,因为这样可以从方法签名上就可以看出该方法调用不会影响该对象的状态)
  • 巧用final关键字(修饰常量,修饰使用频繁,终态,方法体不大的方法(转为内联方法))(final关键字一般在定义常量和方法用的比较多,而大多数人对final的理解往往是在不可变性上,而final对性能优化也有很大的作用)声明常量的时候加上 static final

    • 比如:static int AGE = 10;当10在后面被引用时,这时会有一个字段查找的过程,对于int类型也就是查找方法区中的整型常量池,而对于final的常量,则省去了这个过程,比如:static final int AGE = 10;在使用到AGE的地方将直接用10代替。
    • 对与final关键字,还有一个强大的作用,就是对那些使用频繁、已经确定为终态的方法定义final,这样有什么好处呢?说这个前先来说说java中方法的执行过程吧,当调用某个方法时,首先这个方法会入栈,执行完毕后,这个方法出栈,资源释放,而这个过程内部其实是内存地址的转移过程,当执行入栈的方法时,其实就是把程序的执行地址转移到该方法存放的内存地址中,而做此操作前,还有必须进行原先程序执行的内存地址保存过程,当方法执行完出栈后则继续按保存的地址继续执行程序,而这个过程,就是方法的调用过程。所以,方法的调用过程实际上是需要空间和时间的,而对于同一个方法的频繁调用的优化实际上就是使用内联的办法。又说到内联函数,内联函数实际上是在编译期做的优化,编译器会将标为为内联的函数在其调用的地方直接用整个函数体进行替换掉,这就省去了函数调用所耗去的时间资源了,而换来的却是目标代码量的增加,所以内联这种优化策略实际上是采取了以空间换时间的策略,对于移动端来说,巧用内联函数实则非常有益。而要是一个函数成为内联函数,就是将它定义为final,这样在程序编译时,编译器会自动将final函数进行内联优化,那么在调用该函数时则直接展开该函数体进行使用。
  • 优先考虑系统中提供的代码而不是自己写(系统内置了许多非常方便的api供我们使用,比如:System、Arrays、Collections、String等内置了许多方法api)

  • 慎用异常(因为抛异常时都会执行fillInStackTrace();方法,该方法作用就是重新调整堆栈,这使得没有必要用异常的地方一定要避免使用)

内存泄漏

  • (static修饰的字段 生命周期和application,单例,进程 一样长,应用退出后只要进程没被销毁就还存在)
  • (非静态的内部类和非静态匿名内部类对象都会隐式地持有其外部类的引用 OuterClass$InnerClass)java中的匿名类和匿名内部类
  • 生命周期短的应用被生命周期长的应用所引用 就会出现内存溢出(内存浪费)

  • 检测内存泄漏工具

  • 定义:当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。

  • Android中常见的内存泄漏

    • 单例造成的内存泄漏(需要传入context的时候)

      public class SingleYou{
          private static SingleYou instance;
          private Context mContext;
      
          private SingleYou(Context context){
              mContext = context.getApplicationContext();//使用全局application(单例的生命周期和Application的一样长,生命周期和应用进程一样长)
          }
          public static SingleYou getInstance(Context context){
              if(null == instance){
                  synchronized(SingleYou.class){
                      if(null == instance){
                          instance  = new SingleYou(context);
                      }
                  }
              }
              return instance;
          }
      }
      
    • 非静态的内部类的对象和匿名内部类对象都是会持有指向外部类对象的引用**(常见的匿名内部类对象有new Thread(),new Runnable(),new Thread(),new AsyncTask

一些建议

  • 1、不要让生命周期长于Activity的对象持有到Activity的引用,对于生命周期比Activity长的对象如果需要应该使用ApplicationContext
  • 2、在涉及到Context时先考虑ApplicationContext,当然它并不是万能的,对于有些地方则必须使用Activity的Context,对于Application,Service,Activity三者的Context的应用场景如下:

    **其中:**NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建
  • 3、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏
  • 4、尽量不要在Activity中使用非静态内部类,因为非静态内部类会隐式持有外部类实例的引用(具体可以查看细话Java:”失效”的private修饰符了解)。如果使用静态内部类,将外部实例引用作为弱引用持有。对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:

    - 将内部类改为静态内部类
    - 静态内部类中使用弱引用来引用外部类的成员变量
    
  • 5、对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null
  • 6、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期
  • 7、用完一定记得释放

READ MORE:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值