Android articles 翻译之一:Avoiding Memory Leaks

Avoiding Memory Leaks

Android applications are, at least on the T-Mobile G1, limited to 16 MB of heap. It's both a lot of memory for a phone and yet very little for what some developers want to achieve. Even if you do not plan on using all of this memory, you should use as little as possible to let other applications run without getting them killed. The more applications Android can keep in memory, the faster it will be for the user to switch between his apps. As part of my job, I ran into memory leaks issues in Android applications and they are most of the time due to the same mistake: keeping a long-lived reference to a Context .

至少在T-Mobile G1上Android应用在堆上分配的内存大小被限制16MB以内。对于手机来说,这是个不小的内存,但是这仍然远远不能满足一些开发者的需求。但是,即使你不打算使用所有的内存空间,你也应该尽可能地少用内存,从而使得其他应用能够运行而不是被杀掉。因为Android能够在内存中保持的应用越多,那么用户切换应用的速度就会越快。作为我工作的一部分,我 在做 android应用开发的时候 也会陷入 存泄漏的问题中 ,大多数时候内存的泄漏都是由于犯了相同的错误:长期持有了一个Context的引用。


On Android, a Context is used for many operations but mostly to load and access resources. This is why all the widgets receive a Context parameter in their constructor. In a regular Android application, you usually have two kinds of Context, Activity and Application . It's usually the first one that the developer passes to classes and methods that need a Context:

Android上 ,Context可以用于很多操作,但是大部分时候是用来加载以及使用资源。这就是为什么所有的widgets在他们的构造函数中接受一个Context参数。在一般的android应用中,你通常有两种Context:分别是Activity和Application。通常的,当我们的类和方法需要使用到context时,我们传递的是Activity这个context:

 

 


 

 

This means that views have a reference to the entire activity and therefore to anything your activity is holding onto; usually the entire View hierarchy and all its resources. Therefore, if you leak the Context ("leak" meaning you keep a reference to it thus preventing the GC from collecting it), you leak a lot of memory. Leaking an entire activity can be really easy if you're not careful.

这意味着views拥有一个对整个activity的引用,也就是引用了你的activity所拥有的一切;通常的,这指的是完整的视图层级结构以及所有它的资源。因此,如果你泄露了一个Context(“ 泄漏 ”意味着你保持着它的一个引用,从而使它不能被垃圾回收机制回收),就意味着你 泄漏 了很多的内存。如果你不小心, 泄漏 activity的所有资源真的非常容易。


When the screen orientation changes the system will, by default, destroy the current activity and create a new one while preserving its state. In doing so, Android will reload the application's UI from the resources. Now imagine you wrote an application with a large bitmap that you don't want to load on every rotation. The easiest way to keep it around and not having to reload it on every rotation is to keep in a static field:

屏幕的方向 发生改变的时候,系统默认将会销毁当前的activity并且创建一个新的activity同时保持着原有的状态。在做这个的时候,Android会从资源重新加载应用的UI。现在,想象一下你写了一个应用,这个应用有一张很大的bitmap。你不想再每一次旋转的时候重新加载它。最简单的方法让bitmap持续作用而不随每一个方向而重新加载 ,就是 把它放进一个静态域:

 


This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)

这段代码很快,但是错误也很严重:它泄漏了第一个activity,这个在第一次屏幕改变时被创建的activity。当一个Drawable被关联到一个view上,这个view就相当于在drawable上设置的一个回调。在上面的代码片段中,这表示drawable有一个TextView的引用,而这个TextView又拥有一个activity的引用(Context),activity依次引用了几乎所有的东西(取决于你的代码)。


This example is one of the simplest cases of leaking the Context and you can see how we worked a round it in the Home screen's source code (look for the unbindDrawables() method) by setting the stored drawables' callbacks to null when the activity is destroyed. Interestingly enough , there are cases where you can create a chain of leaked contexts, and they are bad. They make you run out of memory rather quickly.

这个例子展示了一个最简单的Context 泄漏的情况,你可以在Home screen 的源码中看到我们是如何解决这个问题的( 查找unbindDrawables() 方法) ,这就是当activity 被销毁的时候将drawables 的回调设为null 。有趣的是,你可能创造出一系列context 泄漏 的情况有很多,这非常糟糕。他们会是你很快内存溢出。


There are two easy ways to avoid context-related memory leaks. The most obvious one is to avoid escaping the context outside of its own scope. The example above showed the case of a static reference but inner classes and their implicit reference to the outer class can be equally dangerous. The second solution is to use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. If you plan on keeping long-lived objects that need a context, remember the application object. You can obtain it easily by calling Context.getApplicationContext() or Activity.getApplication() .

 

有两种简单的方法来避免context 相关的内存泄漏。最显著地一个是避免context 逃出他自己的范围之外。上面的例子就展示了使用静态引用的情况,而内部类和他们对外部类的的隐式引用也是同样危险的。第二种方法是使用Application context 。这个context 的生存周期和你的应用的生存周期一样长,而不是取决于activity 的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context ,记得使用application 对象。你可以通过调用Context.getApplicationContext() or Activity.getApplication() 来获得。


In summary, to avoid context-related memory leaks, remember the following:

  • Do not keep long-lived references to a context-activity (a reference to an activity should have the same life cycle as the activity itself)
  • Try using the context-application instead of a context-activity
  • Avoid non-static inner classes in an activity if you don't control their life cycle, use a static inner class and make a weak reference to the activity inside. The solution to this issue is to use a static inner class with a WeakReference to the outer class, as done in ViewRoot and its W inner class for instance
  • A garbage collector is not an insurance against memory leaks

总而言之,想要避免context 相关的内存 泄漏 ,记住以下几点:

· 不要对activity 的context 长期引用( 一个activity 的引用的生存周期应该和activity 的生命周期相同)

· 试着使用关于application的 context 来替代和activity相关的 context

· 如果一个acitivity 的非静态内部类的生命周期不受控制,那么避免使用它;使用一个静态的内部类并且对其中的activity 使用一个弱引用。解决这个问题的方法是使用一个静态的内部类,并且对它的外部类有一个WeakReference ,就像在ViewRoot 中内部类W 所做的就是这么个例子。

·   垃圾回收器不能处理内存泄露的保障。

 

 

 

PS:如果有任何的理解偏差或者翻译失当的地方,敬请不吝指正!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值