性能优化(二) 内存溢出& 内存泄漏

内存泄漏& 内存溢出

内存泄漏&内存溢出的区别

  • 内存泄漏 memory leak
    • 系统无法释放已经申请的内存
  • 内存溢出 out of memory (内存不足)
    • 申请内存时系统没有足够的内存供其使用
  • 内存泄露是导致内存溢出的原因之一,内存泄漏过多 最终会导致内存溢出
  • 内存泄露可以通过完善代码来避免;内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。

内存泄漏分类

  • 常发性内存泄漏
    • 内存泄漏的代码经常被执行到
  • 偶发性内存泄漏
    • 内存泄漏的代码在某些特定条件下执行
  • 隐式内存泄漏
    • 程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存,多表现在服务端,因为服务端24*n运行
  • 一次性内存泄漏
    • 内存泄漏的代码在程序运行过程中只执行一次

导致内存泄漏的原因& 解决办法

总体来说 都属于资源未释放

  1. 长生命周期持有短声明周期的引用

    • 比如单例模式持有Activity的context
      • 解决办法
      • 将activity的context 换成ApplicationContext
  2. 非静态内部类 和 匿名类 都会潜在的引用它们所属的外部类

    • 但是,静态内部类却不会。如果这个非静态内部类实例做了一些耗时的操作,就会造成外围对象不会被回收,从而导致内存泄漏。

      • 解决办法

        • 将内部类定义成静态类即可

          • 如果有强引用Activity中的属性,则将该属性的引用方式改为弱引用;

            • new WeakReference<View>("Activity中的属性");
              
          • 在业务允许的情况下,当Activity执行onDestory时,结束这些耗时任务;

  3. 连接资源没有被关闭,图片资源没有被释放,View 没有被销毁,属性动画没有close

    • 比如 数据库的Cousor ,Io 流 ,网络连接
      • 解决方案
        • 在不用时将资源调用close关闭即可 一般try catch finally 中的finally中关闭 此类连接
    • Bitmap 图片资源没有释放
      • 解决方案
        • 不用时调用recycle 释放即可
  4. 集合数据未及时清理导致的内存泄漏

    • 当集合数据过大导致内存泄漏
      • 解决方案
        • 当集合中数据不在使用,及时清空
  5. 注册API中的监听器,回调没有解除注册

    • 例如 动态注册广播,在activity退出时就要解除注册

      • 解决方案
        • 及时解除注册
    • 例如WebView sdk5.1会产生内存泄漏

      • 原因AwContents 类中注册了component callbacks,但是未正常反注册而导致的。导致WebView的ContentViewCore中的成员变量mContainerView引用着AccessibilityManagermAccessibilityStateChangeListeners导致activity不能被回收造成了泄漏。

      • 具体原因 mAccessibilityStateChangeListeners没移除mComponentCallbacks没有解除注册

        • onAttachedToWindow的时候调用mContext.registerComponentCallbacks(mComponentCallbacks)进行注册,
          onDetachedFromWindow中反注册

        • 但是在onDetachedFromWindow中有可能直接返回所以有可能不会执行反注册

        •     public void onDetachedFromWindow() {
                      if (isDestroyed()) return;
                      if (!mIsAttachedToWindow) {
                          Log.w(TAG, "onDetachedFromWindow called when already detached. Ignoring");
                          //直接返回
                          return;
                      }
                      mIsAttachedToWindow = false;
                      hideAutofillPopup();
                      nativeOnDetachedFromWindow(mNativeAwContents);
                      mContentViewCore.onDetachedFromWindow();
                      updateHardwareAcceleratedFeaturesToggle();
                      if (mComponentCallbacks != null) {
                          mContext.unregisterComponentCallbacks(mComponentCallbacks);
                          mComponentCallbacks = null;
                      }
                      mScrollAccessibilityHelper.removePostedCallbacks();
                      mNativeGLDelegate.detachGLFunctor();
                  }
          
        • 解决方案

          • 1 . 在销毁webview前一定要onDetachedFromWindow,我们先将webview从它的父view中移除再调用destroy方法,代码如下:

            @Override
            protected void onDestroy() {
               super.onDestroy();
               if (mWebView != null) {
                  ViewParent parent = mWebView.getParent();
                  if (parent != null) {
                     ((ViewGroup) parent).removeView(mWebView);
                  }
                  mWebView.removeAllViews();
                  mWebView.destroy();
                  mWebView = null;
               }
            }
            
            1. 利用framelauyout动态加载webview destory中先移除 在销毁 创建webview 用Application的context
            1. 单独开进程
            • activity中指定 进程名

            • 导致问题 application多次启动 可以获取当前进程名称判断是否需要初始化设置

  6. HandlerAsyncTask导致的内存泄漏

    • Handler 解决方案

      • 静态内部类 将Handler 定义为静态内部类

        • java总静态内部类不持有外部类引用
      • 弱引用 将引用的activity中的属性保存为弱引用 比如引用了Activity

        new WeakReference<Activity>(activity);
        
      • 移除所有信息调用removeCallbacksAndMessages

      • Handler产生原因

        • 当我们Activity销毁的时,有可能Handler还在继续等待执行未完成message,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即remove掉所有的消息和回调,以避免发生内存泄漏。
        • message 持有了handler对象
    • AsnycTask 解决方案

      • 将其定义成静态内部类
      • 引用activity内的属性 保存为弱引用 参考Handler
      • AsyncTask产生内存泄漏原因
        • 当我们Activity销毁的时,有可能Timer还在继续等待执行TimerTask,它持有Activity的引用不能被回收,因此当我们Activity销毁的时候要立即cancelTimerTimerTask,以避免发生内存泄漏。
  7. String字符串拼接的问题

    • String 每一次拼接都会创建一个新的对象,造成内存占用过大
    • 解决方案
      • 使用StringBuffer
  8. 改变哈希值

    • 当对象被存储进HashSet集合后,就不能在对这个对象中参与计算哈希值的字段进行修改,一旦进行修改对象的哈希值会发生变化
      • 这种情况下,即使Contains方法使用当前对象的当前引用也不能检索到对象,会返回找不到该对象,也会导致该对象一直在Hashset中,无法删除,如果数据过多就会导致内存泄漏
  9. listview 构造Adapter时,没有使用缓存的 convertView

    • 解决方案 在实现adapter时 使用convertView缓存

总结

  • 应保持良好的编程习惯,根据需求及时的释放资源,根据需求选择适用的数据集合,比如android提供的SparseArrayArrayMap,比hashmap占用的内存更小

内存溢出 导致的原因

  1. 内存泄漏过多导致

  2. 系统分配内存过小

    • 可能还有其他的,欢迎留言

内存溢出解决方案

  • 保持良好的代码习惯,及时释放资源

  • 向系统申请更大的内存空间

    • AndroidManifest.xml中设置

    • android:largeHeap=”true” 为true 申请512M, false 申请192M
      
  • 使用更轻量级的数据结构

    • 使用轻量级的数据结构,替代hashmap,用sparseArray,Hashmap会导致一些没必要的自动装箱和拆箱。
  • 避免频繁的创建对象

    • 比如自定义view ondraw 就应该避免创建对象的操作
  • 资源文件图片与目录不匹配

    • ldpi QVGA (240×320)
    • mdpi HVGA (320×480)
    • hdpi WVGA (480×800)
    • FWVGA (480×854)
    • xhdpi 720P(1280x720)
    • xxhdpi 1080p(1920x1080 )
    • xxxhdpi 4K(3840×2160)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值