zoukankan      html  css  js  c++  java
  • 性能优化-内存泄露常见例子

    之前说了内存泄漏和检测工具,这里就记录一下常见的内存泄露有哪些

    前言

    在举例子以前,需要明白两个概念

    • 内存泄露(Memory Leak):某些对象已经不再使用,但仍然直接或间接的被引用到GC ROOT中,此时GC没法对其进行回收,就造成了内存泄露
    • 内存溢出(OOM):当应用占用了大于虚拟机分配的内存空间时,会造成内存溢出

    静态变量引起的内存泄露

    当调用getInstance时,如果传入的contextActivitycontext。只要这个单利没有被释放,那么这个Activity也不会被释放一直到进程退出才会释放

    public class CommUtil {
    	private static CommUtil instance;
    	private Context context;
    	private CommUtil(Context context){
    		this.context = context;
    	}
    	public static CommUtil getInstance(Context mcontext){
    		if(instance == null){
    			instance = new CommUtil(mcontext);
    		}
    		return instance;
    	}
    }
    

    非静态内部类引起内存泄露(包括匿名内部类)

    错误的示范:

    public void loadData(){
    	new Thread(new Runnable() {
    		@Override
    		public void run() {
    			while(true){
    				try {
    					//处理数据
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}).start();
    }
    

    当调用这个方法的时候,这个内部类会持有调用方法的类,可能就会造成内存泄露
    解决方案:将非静态内部类修改为静态内部类(静态内部类不会隐式持有外部类)

    public static void loadData(){
    	new Thread(new Runnable() {
    		@Override
    		public void run() {
    			while(true){
    				try {
    					//处理数据
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}).start();
    }
    

    其实就是非静态方法的生命周期短于当前类而造成的内存泄露
    还有另一种情况,线程延迟执行的情况,此时已经加载到执行队列,比如:

    public static void loadData(){
    	new Timer().schedule(new TimerTask() {
    		@Override
    		public void run() {
    			while(true){
    				try {
    					//处理数据
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}
    		}
    	}, 20000);
    }
    

    那么这种情况就需要在onDestroy方法中把timer.cancel掉然后赋空
    在使用Handler时候也存在这样的情况,例如

    private Handler mHandler = new Handler(){
    	@Override
    	public void handleMessage(Message msg) {
    		super.handleMessage(msg);
    		switch (msg.what){
    		case 0:
    			//处理数据
    			break;
    		}
    	}
    };
    

    如果在其外部调用mHandler.sendEmptyMessage(0);或者mHandler.sendMessageAtTime(msg,10000);方法的时候,此时mHandler作为内部类,持有外部类的引用,如果其Activity退出,那么mHandler可能还存在,那么此时就会一直持有Activity,就造成了内存泄露
    解决办法:

    1. 与其他处理方式一样,将其设置为静态内部类
    2. 如果在其内部需要使用外部类的资源时,将Handler设置为弱引用
    private static class MyHandler extends Handler{
    	//private MainActivity mainActivity;//直接持有了一个外部类的强引用,会内存泄露
    	private WeakReference<MainActivity> mainActivity;//设置弱引用保存,当内存一发生GC的时候就会回收
    
    	public MyHandler(MainActivity mainActivity) {
    		this.mainActivity = new WeakReference<MainActivity>(mainActivity);
    	}
    
    	@Override
    	public void handleMessage(Message msg) {
    		super.handleMessage(msg);
    		MainActivity main =  mainActivity.get();
    		if(main == null || main.isFinishing()){
    			return;
    		}
    		switch (msg.what){
    			case 0:
    				//处理数据
    				int b = main.a; //引用外部类的资源
    				break;
    		}
    	}
    }
    

    不需要用的监听未移除会发生内存泄露

    例子1:

    final TextView tv =  (TextView) findViewById(R.id.tv);
    tv.setOnClickListener(); //监听执行完回收对象
    tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
    	@Override
    	public void onWindowFocusChanged(boolean b) {
    		//监听处理,处理完成后要移除监听
    		tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);
    	}
    });
    

    如果要设计ListenerCollector,那么一般使用WeakHashMapWeakHashMap除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值

    public class ListenerCollector {
    
        static private WeakHashMap<View, MyView.MyListener> sListener = new WeakHashMap<>();
    
        public void setsListener(View view, MyView.MyListener listener) {
            sListener.put(view, listener);
        }
    
        public static void clearListeners() {
            //移除所有监听
            sListener.clear();
        }
    
    

    例子2:

    SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
    Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
    sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
    //不需要用的时候移除监听
    sensorManager.unregisterListener(listener);
    

    资源未关闭引起的内存泄露情况

    BroadCastReceiverCursorBitmapIO流自定义属性attribute
    当不需要使用的时候,及时释放资源,否则就会内存泄露

    无限循环动画

    没有在onDestroy中停止动画,否则Activity就会变成泄露对象,比如:轮播图效果

  • 相关阅读:
    Day2 while 循环,格式化输出,运算符,字符串编码
    Day 1 变量,基础数据类型与条件语句
    关于字符的一些看法
    正则的全局问题
    模块——js功能(倒计时,幻灯)
    垂直居中
    不确定宽度元素居中
    css3媒体查询
    less的预处理
    手机幻灯
  • 原文地址:https://www.cnblogs.com/cj5785/p/10664640.html
Copyright © 2011-2022 走看看