避免内存泄露

Android应用程序堆最大为16MB,至少在G1之前是这样(博主补充:Droid为24MB,Nexus One为32MB,Xoom为48MB)。对于手机而言已经是很大的内存空间了,但对于部分开发者而言还是很少的。即便没有将这些内存用完的打算,开发者也应尽量减少内存开销以便其他应用能够在后台运行而不会被强制关闭。这样的话,Android在内存中保存的应用越多,用户在应用间的切换就越快。我工作的一部分就是深入探索Android应用程序的内存泄露问题,大部分时间里,这些问题都是源自同一个错误:对Context(上下文环境)的长时间引用。

        在Android上,Context用于多种操作,但最多的还是用来加载和访问资源。这也是为什么所有的Widges在其构造函数中都有一个Context参数。常规的Android应用中,有两类Context:Activity Context和Application Context,通常前者被开发者传递给需要Context的类和方法。

[java]  view plain copy
  1. @Override  
  2. protected void onCreate(Bundle state) {  
  3.   super.onCreate(state);  
  4.     
  5.   TextView label = new TextView(this);  
  6.   label.setText("Leaks are bad");  
  7.     
  8.   setContentView(label);  
  9. }  
这就意味着那些视图引用了整个Activity及其所拥有的一切:一般是整个视图层和所有资源。因此,如果泄露了这类Context(这里的泄露指的是引用Context,从而阻止了GC(垃圾回收)操作),就泄露了很多内存空间。如果不小心,泄露整个Activity是非常容易的事。

        在进行屏幕方向改变的时候,系统默认做法是保持状态不变的情况下,销毁当前Activity并重新创建一个新的Activity。这样做,Android会从资源文件中重新装载当前应用的UI。现在假设你写了一个带有很大一幅位图的应用,但你不想在每次屏幕旋转时都装载一次位图,最简单的做法就是将其保存在一个静态区域中:

[java]  view plain copy
  1. private static Drawable sBackground;  
  2.   
  3. @Override  
  4. protected void onCreate(Bundle state) {  
  5.   super.onCreate(state);  
  6.     
  7.   TextView label = new TextView(this);  
  8.   label.setText("Leaks are bad");  
  9.     
  10.   if (sBackground == null) {  
  11.     sBackground = getDrawable(R.drawable.large_bitmap);  
  12.   }  
  13.   label.setBackgroundDrawable(sBackground);  
  14.     
  15.   setContentView(label);  
  16. }  
这段代码执行的快,同时也很有问题:在进行第一次屏幕方向改变的时候泄露了第一个Activity所占的内存空间。当一个Drawable连接到一个视图上时,视图被设置为Drawable上的一个回调,在上面的代码片段中,这就意味着Drawable引用了TextView,而TextView又引用了Activity Context,Activity Context又进一步引用了更多的东西(依赖与你的代码)。

        上面这段示例是最简单的泄露Activity Context的情况,你可以到Home Screen's Source Code查看unbindDrawables()方法中看看我们是如何通过在Acitivity销毁时将存储Drawable的回调置为null来解决该问题的。如果再有兴趣的话,某些情况下会产生一个由泄露的Context形成的链,这很糟糕,会很快使得内存耗尽。

        有两种方法可以避免Activity Context相关的内存泄露:最明显的一种是避免在Activity Context自身的范围之外对其进行引用。上面这段示例展示了静态引用的情况,但对内部类和外部类的隐式引用同样都是危险的。第二种解决方法是用Application Context,因为该Context与应用的生命周期一样长,并不依赖Activity的生命周期。如果想拥有一个生命期足够长的object(对象),但却需要给其一个必须的Context的话,别忘了Application Object。获取Application Context的方法很简单:执行Context.getApplicationContext()或Activity.getApplication()。

        总之,为了避免Activity Context相关的内存泄露,记住下面几条:

1)、不要长时间引用一个Activity Context(引用周期应与Acitivity的生命周期一样长)

2)、尝试使用Application Context代替Acitivity Context

3)、在Activity中,避免使用你无法控制其生命周期的非静态的内部类,使用静态的内部类,并对Activity内部进行弱引用。就是在静态的内部类中对外部类进行弱引用,就如在ViewRoot及其W内部类中的做法那样

4)、垃圾回收(GC)无法保证内存泄露

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的ThreadLocal变量是一种线程本地变量,它提供了一种在多线程环境下保持变量的独立副本的机制。每个线程都可以独立地修改自己的副本,而不会影响其他线程的副本。这种机制可以避免线程安全问题,并且在某些情况下可以提高性能。 然而,如果在使用ThreadLocal变量时不小心处理,可能会导致内存泄漏。内存泄漏是指在程序中不再使用的对象仍然占用内存空间,无法被垃圾回收器回收,从而导致内存的浪费。 为了避免ThreadLocal变量的内存泄漏,我们需要注意以下几点: 1. 及时清理:在使用完ThreadLocal变量后,应该及时调用remove()方法将其从当前线程中移除。这样可以避免变量的副本一直存在于线程中,占用内存。 2. 使用try-finally块:为了确保在任何情况下都能正确地清理ThreadLocal变量,可以使用try-finally块来确保在使用完后进行清理操作。 下面是一个示例代码,演示了如何正确使用ThreadLocal变量并避免内存泄漏: ```java public class ThreadLocalExample { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { try { threadLocal.set("Hello, ThreadLocal!"); // 使用ThreadLocal变量 System.out.println(threadLocal.get()); } finally { // 清理ThreadLocal变量 threadLocal.remove(); } } } ``` 在上面的示例中,我们使用了try-finally块来确保在使用完ThreadLocal变量后进行清理操作。在finally块中调用remove()方法将变量从当前线程中移除,以避免内存泄漏。 总结一下,为了避免ThreadLocal变量的内存泄漏,我们需要在使用完后及时清理,并且可以使用try-finally块来确保清理操作的执行。这样可以保证ThreadLocal变量的副本不会一直存在于线程中,从而避免内存泄漏的问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值