美文网首页
内存泄漏 快速过一下

内存泄漏 快速过一下

作者: 刘尔泽 | 来源:发表于2017-09-28 18:19 被阅读18次

    java 内存泄漏 基础知识

    java 内存的分配策略

    静态分配, 栈式分配,堆式分配
    对应的

    • 静态存储区,也叫方法区,这个内存区里 主要存放一些 静态数据,全局变量等等,这块内存,程序编译的时候已经分配好了,并且在静态存储区中存储的变量,在整个程序运行期间 都存在
    • 栈区 ,方法执行的时候,方法体内的局部变量,会在栈上创建内存空间,并在方法执行结束之后,这些变量所持有的内存会被自动释放,因为栈内存分配运算,内置于处理器中,所以效率很高,但是栈区的内存空间 容量有限
    • 堆区,又称为动态内存分配,通常就是我们 new 对象出来的内存,这部分内存在不使用的时候,会被 java 的垃圾回收器来负责回收。

    栈区,跟堆区 :
    栈区:在方法 体内定义的基本类型的变量,和对象的引用变量都是在方法的栈内存中分配的,当一段方法中定义变量时,java就会在栈中为该变量分配内存空间,当超过这个变量的作用域时,这变量就无效了,分配给它的内存空间就被释放,这时候栈区以前占着的内存空间就会被其他方法所使用。
    堆区:存放所有 由new 创建的对象,以及数组,在堆中分配的内存,将由java 的垃圾回收器来管理,其中产生的一个数组,或者对象,还可以在栈中定义一个特殊的变量,也就是通过引用变量访问堆中的东西。

    java是如何管理内存的

    java 内存管理,就是对象的分配和释放问题, 通过 new 为对象申请内存空间,所有的对象都是在堆中分配的,gc 自动释放。简化了程序员的工作,但是加重了java虚拟的工作,这也是java 为啥慢的原因之一。 gc 是有很大作用的,为了正确的释放对象,他必须监控每一个对象的运行状态,包括,对象的申请,引用,被引用,复制等等。
    可以把对象考虑为有向图的顶点,将引用关系考虑为有向图的边,从进程顶点开始的一颗有向根树,在有向图中,根顶点可达的对象 都是有效对象,根顶点不可达的对象是可以回收的对象。

    java 中的内存泄漏

    内存泄漏是指无用对象 持续占有内存,或者 无用对象的内存得不到及时的释放,从而造成的内存空间的浪费称为内存泄漏

    Android 中的内存泄漏

    单例
    不恰当的使用单例,会导致内存泄漏

      public class AppManager {
     //有内存泄漏的问题:
     //private static AppManager instance;
    // private Context context;
    //    private AppManager(Context context) {
    //        this.context = context;
    //    }
    //    public static AppManager getInstance(Context context) {
    //        if (instance == null) {
    //            instance = new AppManager(context);
    //        }
    //        return instance;
    //    }
    //    修复内存泄漏的写法:
    private static AppManager instance;
    private Context context;
    
    private AppManager(Context context) {
        this.context = context.getApplicationContext();// 使用Application 的context
    }
    
    public static AppManager getInstance(Context context) {
        if (instance == null) {
            instance = new AppManager(context);
        }
        return instance;
      }
    }
    

    匿名内部类
    非静态内部类默认会持有外部类的引用,应该设为 静态内部类

    handler
    handler 是非静态内部类。
    解决:把handler 设为静态内部类,同时 在handler 的内部持有activity 外部类的弱引用。

    //容易造成内存泄漏的写法:

    //    private Handler mHandler = new Handler() {
    //        @Override
    //        public void handleMessage(Message msg) {
    //            //...
    //       }
     //    };
    //
    //    @Override
    //    protected void onCreate(Bundle savedInstanceState) {
    //        super.onCreate(savedInstanceState);
    //        setContentView(R.layout.activity_main);
    //        loadData();
    //    }
    //
    //    private void loadData() {
    //        //...request
    //        Message message = Message.obtain();
     //        mHandler.sendMessage(message);
     //    }
      //
      //    static class TestResource {
      //        private static final String TAG = "";
    //        //...
    //    }
    

    // 修复内存泄漏的方法:

     ★import java.lang.ref.WeakReference;
    private MyHandler mHandler = new MyHandler(this);
    private TextView mTextView ;
    private static class MyHandler extends Handler {
        private WeakReference<Context> reference;
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
    //            activity.mTextView.setText("");
            }
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    
    private void loadData() {
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
         mHandler.removeCallbacksAndMessages(null);
     }
     }
    

    避免使用 static 成员变量

    如果把成员变量声明为static,那么他的生命周期,和整个App的 生命周期是一致的,如果App进程 设计上是常驻内存的,那即使App 切到后台,这部分的static 变量也是不会被释放的,按照我们现在App内存管理机制,占内存较大的后台进程,将优先被回收,所以说当你的进程被回收了之后,你所存的那些变量,那些数据是不安全的。
    所以这类问题,在类设计的时候要考虑好,是不是要在初始化的时候去设为静态成员,是不是可以考虑懒加载。如果一定要使用,一定要对这些变量的生命周期做一个管理。

    资源未关闭造成的内存泄漏
    使用广播啊,contenProvider ,文档啊,游标,socket,Bitmap,在 生命周期销毁的时候,关闭和注销这些资源

    AsyncTask 造成的内存泄漏
    跟 handler 一样,需要在 声明周期销毁的时候,cancle

    Bitmap c 端的内存,需要 recycle()

    内存管理机制

    从操作系统的角度来说,内存就是一块数据存储区域,而且属于被操作系统调度的资源。
    分配内存:操作系统会为每一个进程分配一个合理的内存大小,从而保证每一个进程正常使用。
    回收机制:操作系统里的内存回收机制,在内存不足的时候会有一个合理的回收再分配的作用。

    Android 内存管理机制

    • 分配机制
      安卓 在为每一个进程分配内存的时候,采用了一个弹性的分配方式,也就是说:系统一开始,不会为这个APP分配太多的内存,而是为每一个APP进程分配一个小额的量,而这个小额的量是根据每一个移动设备的物理RAM尺寸大小决定的,随着App的不断运行,当前内存不足,android 就会继续为 app 分配更多的内存,当然是有限的。总之,android系统的内存分配机制,就是让更多的进程存活在内存中,当用户下一次启动该APP进程时候,就不需要重新创建进程,而是恢复已有的进程就可以了,提高了体验。

    • 回收机制
      android对内存的使用,是尽最大限度地使用,这是继承Linux 的特点。
      当系统发现内存不足的时候,就会杀死其他进程。为新进程分配内存
      所以就有个优先级
      前台进程
      可见进程
      服务进程 :会开启一些服务
      前三个不会被杀死
      后台进程:存放在一个缓存列表中,数据结构是LRU,先杀死的处于列表的尾部
      空进程 :为了平衡系统的性能,Android不会保存这些进程。

    还有个回收效益的概念,当Android系统开始杀死进程的时候,系统会判断每一个进程杀死后带来的回收效益,因为android总是倾向于 回收更多的内存,当然杀死的越少越好。

    内存管理的目标

    • 更少的占用内存
    • 在合适的时候,合理的释放系统资源
    • 在合理的生命周期中,保存或者还原重要的数据

    内存优化的方法

    • service 完成任务后,尽量停止它。因为他是优先级比较低的服务进程,影响内存回收, 用IntentService 替代 service,因为IntentService 继承service内部开启子线程,可以做耗时操作,还可以自动退出。而不是像Service 要手动 stopService。

    • 在Ui 不可见的时候,释放掉一些只有Ui使用的资源,有个 OnTreeMemory() 通知App回收资源

    • 内存紧张的时候,也是用OnTreeMemory() 通知App回收资源

    • Bitmap 导致的,根据当前设备的分辨率来压缩bitmap,使用bitmap之后 使用recycle() 释放c内存,使用软引用,使用LRU。

    • 使用针对内存优化过的数据结构,替代hashmap 的 SparseArray、ArrayMap,同时,尽量少用 枚举,消耗的内存是常量的两倍

    • 避免使用依赖注入框架,使用這些框架 除了方便,也会带来一些扫描注解带来的系统资源

    • 使用ZIP 对齐的APK

    • 使用多进程,定位,push,webView。当然多进程 的安全,数据传输 很难的问题

    内存溢出 VS 内存泄漏

    内存溢出,其实就是OOM, 还是bitmap ,比如未压缩的bitmap。压缩啊,使用bitmap的属性,裁剪啊,回收啊。
    内存泄漏, 就是本该被垃圾回收器 gc 的内存没有被回收。dump 分析 扯一下子。

    相关文章

      网友评论

          本文标题:内存泄漏 快速过一下

          本文链接:https://www.haomeiwen.com/subject/pqmmextx.html