工作笔记:滥用static标记引发的内存溢出

本文探讨了一次Android应用中出现的内存溢出问题,通过分析发现是由于过度使用static变量导致旧对象未被垃圾回收器回收。文中给出了通过结束进程的方式来解决此问题,并讨论了static变量使用的注意事项。
  前天同事问了我一个问题,说替换Android程序中的图片资源会不会引起程序错误。至少我是没有碰到过这种问题。在res/drawable目录下无论是使用jpg、gif还是png格式,都没引发过任何错误。
  晚上的时候,同事又将运行错误时的log文本发给我。log上描述的是程序运行到某个draw方法时发生了异常,程序无法分配到足够大的内存。我马上问同事,他替换的图片到底有多大。他告诉我说,是20k。20k这样大小的图片,以前做S40的时候,感觉已经是很奢侈的大小了,但是在 Android平台上,这样的大小算是比较小的了。根本不足以引起内存溢出这样的错误。
  于是同事又将项目源代码给我发了过来。限于出租屋悲剧的网速,费了不少周章。
  我运行程序的时候,同事告诉我,退出程序再运行的时候就会引发这个错误。我依言行事,发现果然如此。便打开Eclipse的heap观察功能,查看该程序运行时对内存的消耗情况。发现退出之后第二次运行的时候,占用的内存要比第一次高很多。于是便想程序本身有问题,同事将图片替换后只是恰好使第二次运行时内存的占用超越可以正常运行的临界值,从而引发错误而已。
  观察程序的源文件的时候,发现基本都是类似下面这样的代码:
public class AObject {
public static BObject b;
public static CObject c;
public static AObject  Instance;
 
public AObject(){
b = new BObject();
c = new CObject();
Instance = this;
}
}
  对这个结构我表示很费解。每次构造对象的时候,都要将类成员重新实例化一遍?从Instance这个变量名上看,这个类在程序运行期间应该只会生成一个实例才对。既然如此,何必将所有的成员变量都声明为static呢?
  被声明为static类型的对象生命周期是非常长的。在Android之中,通常退出程序的时候只是调用Activity的finish()方法,而进程是不会像Java SE或是Java ME中那样被杀死的。因为进程一直存在,所以依附于类的static静态成员并不会被垃圾回收器回收。如果一个Activity执行了finish()方法,但是它本身作为类的static的成员存在的话,垃圾回收器根本不会将它回收!如果这个Activity的所有成员对象都存在static类型的引用的话,情况更糟。执行了finish()方法之后,这些成员对象依旧不会被回收,而继续占用内存空间。
比如说:
  在OnCreat()方法中如果有以下语句:
  text = (TextView)findByID(R.id.text);//text事先被声明为static类型
  在该Activity被销毁之后,这个TextView对象因为在类中保有一份引用,所以不会被垃圾回收器回收。
  了解Java的垃圾回收机制的,都知道如果将一份非null的引用指向新对象的时候,这个引用原本指向的对象并不会立即被垃圾回收器回收。所以,这样大量使用static声明的类,在对象实例化的时候,消耗的内存是惊人的。(实际上程序的本意是运行时该类只有一个对象)
  原本我想建议同事修改这个程序的结构,后来发现,这根本是个费力不讨好的工作。因为在这个程序中遍布这样的结构,如若贸然修改,恐怕会产生更多不可预知的错误。
  最后,我给出的方案是在程序退出时执行杀死进程操作:
  finish();
  android.os.Process.killProcess(android.os.Process.myPid()); //杀死当前进程
  当然,这样的方案看上去并不是最佳的。因为随意使用杀死当前进程操作,可能会引起某些不可预知的错误。就像在windows的任务管理器中强行结束进程一样。不过面对这样的代码,似乎这是最佳方案了。进程被结束,不管你是声明为static,还是别的,统统要释放内存给系统。程序下一次运行时就是真正的初始化,而不受到static类型对象的干扰了。
  在合适的情况下,将某一些成员声明为static类型,可以减少程序对内存的占用,减少反复创建对象所需要的时间。但是因为static对象生命周期较长,使用时应该谨慎。
  另外:
  a=new A();
  a=new A();
  语句执行到这里的时候,其实内存中会有两份A类的对象,而不是一份。Java的内存回收机制虽好,却不能保证绝对不会发生内存泄露这样的惨剧。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值