static关键字所导致的内存泄漏问题

大家都知道内存泄漏和内存溢出是不一样的,内存泄漏所导致的越来越多的内存得不到回收的失手,最终就有可能导致内存溢出,下面说一下使用staitc属性所导致的内存泄漏的问题。

在dalvik虚拟机中,static变量所指向的内存引用,如果不把它设置为null,GC是永远不会回收这个对象的,所以就有了以下情况:

 

[java] view plain copy
 
  1. public class SecondActivity extends Activity{  
  2.     private Handler mHandler = new Handler(){  
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             super.handleMessage(msg);  
  6.             SecondActivity.this.finish();  
  7.             this.removeMessages(0);  
  8.         }  
  9.     };  
  10.   
  11.     private static Haha haha;  
  12.     @Override  
  13.     protected void onCreate(Bundle savedInstanceState) {  
  14.         super.onCreate(savedInstanceState);  
  15.         haha = new Haha();  
  16.         mHandler.sendEmptyMessageDelayed(0,2000);  
  17.     }  
  18.   
  19.     class Haha{  
  20.   
  21.     }  
  22. }  

非静态内部类的静态引用。然后在2秒之后我们要finish掉这个activity,会造成什么问题呢?我们知道,内部类和外部类之间是相互持有引用的,SecondActivity实例持有了haha的引用,但这里haha是用static修饰的,上面说了,虚拟机不会回收haha这个对象,从而导致SecondActivity实例也得不到回收,造成内存溢出。

 

这货还在这得不到回收。

怎么解决这个问题呢,很简单,只要在Activity的onDestroy方法里把haha设为null就行啦

[java] view plain copy
 
  1. protected void onDestroy() {  
  2.     super.onDestroy();  
  3.     if(haha!=null){  
  4.         haha = null;  
  5.     }  
  6. }  

 

 

那么还有另外一种情况,单例的问题。单例也是用了其static属性,很多单例,往往需要用到context对象,而又是通过传值的方式获得,比如:

先来一个单例

 

[java] view plain copy
 
  1. public class SingleInstanceF {  
  2.     private static SingleInstanceF single;  
  3.     private  Context context;  
  4.   
  5.     private SingleInstanceF(Context context){  
  6.         this.context = context;  
  7.     }  
  8.   
  9.     public static SingleInstanceF getInstance(Context context){  
  10.         if(single==null){  
  11.             single = new SingleInstanceF(context);  
  12.         }  
  13.         return single;  
  14.     }  
  15.   
  16. }  

再来一个Activity来用它,context传入一个this,再2秒之后关闭Activity

 

 

[java] view plain copy
 
  1. public class ThirdActivity extends Activity{  
  2.     private Handler mHandler = new Handler(){  
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             super.handleMessage(msg);  
  6.             ThirdActivity.this.finish();  
  7.             this.removeMessages(0);  
  8.         }  
  9.     };  
  10.     @Override  
  11.     protected void onCreate(Bundle savedInstanceState) {  
  12.         super.onCreate(savedInstanceState);  
  13.         SingleInstanceF instanceF = SingleInstanceF.getInstance(this);  
  14.         mHandler.sendEmptyMessageDelayed(0,2000);  
  15.     }  
  16. }  

 

 

后来我们发现这货还在,又是得不到回收:

怎么办呢,还是像上面那样,把静态对象设置为null,或者我们传入context的时候,别传this了,this可是当前Acitvity啊,传Application Context即可。但是不是都可以传Application Context呢,明显不是,有的事是Application Context干不了的,这个得看具体情况而定。

自我总结:

内存泄漏概述

内存泄漏指的是程序在运行过程中,一些不再使用的内存空间无法被垃圾回收机制回收,从而导致内存占用不断增加,最终可能引发内存溢出(OutOfMemoryError)。static 关键字在 Java 中用于修饰类的成员(变量、方法、代码块等),被 static 修饰的成员属于类本身,而不是类的实例。如果 static 成员持有对对象的强引用,且这些对象不再使用时,由于 static 成员的生命周期与类相同,会导致这些对象无法被垃圾回收,进而造成内存泄漏。

示例代码及分析

示例一:静态集合持有对象引用

import java.util.ArrayList;
import java.util.List;

public class StaticCollectionMemoryLeak {
    // 静态集合,用于存储对象
    private static List<Object> staticList = new ArrayList<>();

    public void addObjectToStaticList(Object obj) {
        staticList.add(obj);
    }

    public static void main(String[] args) {
        StaticCollectionMemoryLeak example = new StaticCollectionMemoryLeak();
        for (int i = 0; i < 100000; i++) {
            // 创建大量对象并添加到静态集合中
            Object obj = new Object();
            example.addObjectToStaticList(obj);
            // 后续不再使用这些对象,但静态集合仍持有引用
        }
        // 此时,即使这些对象不再使用,由于静态集合的引用,它们也无法被垃圾回收
    }
}

分析

  • staticList 是一个静态的 ArrayList,它会一直存在于内存中,直到类被卸载。
  • main 方法中,创建了大量的 Object 对象并添加到 staticList 中。当这些对象不再被程序使用时,由于 staticList 仍然持有它们的引用,垃圾回收机制无法回收这些对象,从而导致内存泄漏。

示例二:静态内部类持有外部类实例引用

public class StaticInnerClassMemoryLeak {
    private byte[] largeData = new byte[1024 * 1024]; // 1MB 的数据

    // 静态内部类
    private static class StaticInnerClass {
        private StaticInnerClassMemoryLeak outer;

        public StaticInnerClass(StaticInnerClassMemoryLeak outer) {
            this.outer = outer;
        }
    }

    public static void main(String[] args) {
        StaticInnerClassMemoryLeak outer = new StaticInnerClassMemoryLeak();
        StaticInnerClass inner = new StaticInnerClass(outer);
        // 后续不再使用 outer 对象,但静态内部类的实例仍持有其引用
        outer = null; 
        // 此时,由于静态内部类的引用,outer 对象无法被垃圾回收
    }
}

分析

  • StaticInnerClass 是一个静态内部类,它持有外部类 StaticInnerClassMemoryLeak 的实例引用。
  • main 方法中,创建了外部类实例 outer 和静态内部类实例 inner,并将 outer 传递给 inner。当将 outer 引用置为 null 时,由于 inner 仍然持有 outer 的引用,outer 对象无法被垃圾回收,从而导致内存泄漏。

总结

static 关键字导致的内存泄漏主要是因为 static 成员的生命周期与类相同,当它们持有对对象的强引用时,即使这些对象不再被程序使用,也无法被垃圾回收。为了避免这种情况,应尽量避免在 static 成员中持有不必要的对象引用,或者在对象不再使用时及时清除 static 成员中的引用。

posted @ 2017-06-01 01:36  皇问天  阅读(2779)  评论(0)    收藏  举报