怎样避免类存泄露

Android应用程序,最多能分配16M的堆内存,到少在T-moblie G1上是这样的。16M的内存对某些开发者来说可能是一个很小的空间,

但它对手机来说的确是一个比较大的内存开销。即使没有打算用光所有的内存,你也要尽量使用更少的空间,以使得其他的就用程序正常

运行,不因为内存不足而被杀死。内存中能同时运行更多的应用程序,你切换各个应用程序的速度就越流畅。我在工作中,一次偶然的机

会碰到了应用程序中内存泄露的问题,它们大多是同一种情况导致的:保持了一个长期存活的引用到Context。

在Android系统中,Context用来做很多操作,大多数是用来访问和加载资源文件。这就是为什么所有的组件在构造的时候都要接收一个

Context作为参数。在一个普通的Android应用中,你通常有两种上下文Context,Activity和Application,分别是this,ActivityName.this和

getApplicationContext()来得到,他们两个就是范围不一样。通常在类和方法中传递的上下文是第一种。例如下面代码:


@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  setContentView(label);
} 


这就表明某个View(比如上面的TextView)拥有了整个Activity对象的引用,同时也能获得Acitivity对象所能操作的所有内容,通常是指所有的

View集合和资源文件。因此,如果你泄露了Context("leak"泄露是指保持了一个指向它的引用,导致GC不能自动回收这部分内存),那你也就泄露

了很多内存。如果不小心,很容易泄露掉整个Activity占用的内存。如果屏幕的横竖屏发生了改变,系统默认会销毁当前的Activity并且创建一个新

的Activity,与此同时它会保存上一个Activity的状态。这样,Android系统会重新从资源里面加载应用程序的UI。假设你在Application里面写入了一

个很大的Bitmap画片,并且不想在第一次切屏的时候都重新加载,最简单的实现方法是设置一个静态变量,如下代码:

 private static Drawable sBackground;
  
@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  
  TextView label = new TextView(this);
  label.setText("Leaks are bad");
  
  if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);
  
  setContentView(label);
}


这段代码很简便但却是非常错误的。它在第一次屏幕切换的时候泄露掉了第一个Activity。当一个Drawable被绑定到了一个View上时,view就成了这

个Drawable的回调。上面的代码片段表示这个drawable含有一个指向TextView的引用,同时TextView含有一个指向 Activity的引用。这就相当于drawable拥

有了一个指向所有东西的引用。这是一个最简单的Context 内存泄露的例子,你可以看到我们是怎样造成内存泄露的,那就是当Activity销毁的时候让存储的\

drwable的回调指向null。很有趣的是,有可能你会产生一连串的context泄露,这种情况是不好的,它会导航内存很快就被消耗完。这里有两种简便的方法防

止context相关的内存泄露问题。最明显的一种是避免将context弄到它范围之外。上面的例子展示了静态引用的情况,内部类隐式调用外部类也会导致这样的

危险。另外一种方法是使用Application context 。这种context将会与你的Application拥有同样长的生命周期,不会依

赖于Activity的生命周期。如果你打算保持长期存活的对象,并且这个对象需要一个context,那么请记住application对象。你可以通过Context.getApplicationContext()

or Activity.getApplication().很方便的得到application对象。 总之,避免context相关的内存泄露问题,请注意以下几点:

1、不要试图对一个引用了Activity级context的对象保持长期存活。因为Activity的生命周期比较短,不能长期提供你使用。(指向Activity的引用应该和Activity

对象本身有相同的生命周期)

2、试图使用context-application来代替context-activity

3、如果你不能控件它们的生命周期,就不要使用非静态的内部类。使用静态内部类,并且使用弱引用。解决的办法是使用静态的内部类,这个内部类只持有

外部类的一个弱引用。就像ViewRoot里面的W内部类。

4、垃圾回收并不一定能避免内存泄露。

=======================================================================================================================

本人翻译的比较烂,附上高人翻译内容:

1. 关于context的问题,也就是静态成员变量引用资源导致生命周期不一致问题

Android中context可以作很多操作,但是最主要的功能是加载和访问资源。在android中有两种context,一种是applicationcontext,一种是activitycontext,通常我们在各种类和方法间传递的是activitycontext,比如一个activity的onCreate。

Java代码:

 
  1. protectedvoidonCreate(Bundlestate){
  2. super.onCreate(state);
  3. TextViewlabel=newTextView(this);//传递context给viewcontrol
  4. label.setText("Leaksarebad");
  5. setContentView(label);
  6. }

把activitycontext传递给view,意味着view拥有一个指向activity的引用,进而引用activity占有的资源:viewhierachy,resource等。

这样如果context发生内存泄露的话,就会泄露很多内存。

这里泄露的意思是gc没有办法回收activity的内存。

Leakinganentireactivity是很容易的一件事。

当屏幕旋转的时候,系统会销毁当前的activity,保存状态信息,再创建一个新的。

比如我们写了一个应用程序,它需要加载一个很大的图片,我们不希望每次旋转屏幕的时候都销毁这个图片,重新加载。实现这个要求的简单想法就是定义一个静态的Drawable,这样Activity类创建销毁它始终保存在内存中。

实现类似:

java代码:

 
  1. publicclassmyactivityextendsActivity{
  2. privatestaticDrawablesBackground;
  3. protectedvoidonCreate(Bundlestate){
  4. super.onCreate(state);
  5. TextViewlabel=newTextView(this);
  6. label.setText("Leaksarebad");
  7. if(sBackground==null){
  8. sBackground=getDrawable(R.drawable.large_bitmap);
  9. }
  10. label.setBackgroundDrawable(sBackground);//drawableattachedtoaview
  11. setContentView(label);
  12. }
  13. }

这段程序看起来很简单,但是却问题很大。当屏幕旋转的时候会有leak(即gc没法销毁activity)。我们刚才说过,屏幕旋转的时候系统会销毁当前的activity。但是当drawable和view关联后,drawable保存了view的reference,即sBackground保存了label的引用,而label保存了activity的引用。既然drawable不能销毁,它所引用和间接引用的都不能销毁,这样系统就没有办法销毁当前的activity,于是造成了内存泄露。gc对这种类型的内存泄露是无能为力的。

避免这种内存泄露的方法是避免activity中的任何对象的生命周期长过activity,避免由于对象对activity的引用导致activity不能正常被销毁。我们可以使用applicationcontext。applicationcontext伴随application的一生,与activity的生命周期无关。applicationcontext可以通过Context.getApplicationContext或者Activity.getApplication方法获取。

避免context相关的内存泄露,记住以下几点:

避免context相关的内存泄露,记住以下几点:

1.不要让生命周期长的对象引用activitycontext,即保证引用activity的对象要与activity本身生命周期是一样的。

2.对于生命周期长的对象,可以使用applicationcontext。
转载自http://mobile.51cto.com/android-266403.htm


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值