Android开发优化之的强引用、软引用、弱引用的使用
StuartHoffm
8年前
<p style="text-align:center"><img src="https://simg.open-open.com/show/512c645eff352510a315df4da1b09c58.png"></p> <ul> <li> <h3>引言</h3> </li> </ul> <p>早在JDK1.2,Java就把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。</p> <p>但是平时我们的代码中似乎很少出现这些, 而之前还看到过一份代码中, 一个Activity中有一个静态变量持有对自己的弱引用,来达到类似的singleTask的效果.</p> <p>So, 是时候系统的学习一下软引用、弱引用这些,并对我们的代码进行优化了.</p> <ul> <li> <h3>强引用</h3> </li> </ul> <pre> <code class="language-java">String str = new String("xiamin");</code></pre> <p>强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。</p> <p>强引用特点:</p> <ul> <li>强引用可以直接访问目标对象。</li> <li>强引用所指向的对象在任何时候都不会被系统回收。JVM宁愿抛出OOM异常,也不会回收强引用所指向的对象。</li> <li>强引用可能导致内存泄露。</li> </ul> <ul> <li> <h3>软引用 (SoftReference)</h3> 简单用法</li> </ul> <pre> <code class="language-java">MyObject aRef = new MyObject(); SoftReference aSoftRef=new SoftReference(aRef); MyObject anotherRef=(MyObject)aSoftRef.get();</code></pre> <p>java.lang.ref包中提供了几个类:SoftReference类、WeakReference类和PhantomReference类,它们分别代表软引用、弱引用和虚引用。ReferenceQueue类表示引用队列,它可以和这三种引用类联合使用,以便跟踪Java虚拟机回收所引用的对象的活动。</p> <p>如果一个对象只具有软引用,那么如果内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。</p> <pre> <code class="language-java">ReferenceQueue queue = new ReferenceQueue(); SoftReference ref=new SoftReference(aMyObject, queue);</code></pre> <p>那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。</p> <p>在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:</p> <pre> <code class="language-java">SoftReference ref = null; while ((ref = (EmployeeRef) q.poll()) != null) { // 清除ref }</code></pre> <p>当然,我们作为Android开发中使用,正常是用来处理图片这种占用内存大的类的.</p> <p>所以,我们应该这样使用.</p> <pre> <code class="language-java">View view = findViewById(R.id.button); Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher); Drawable drawable = new BitmapDrawable(bitmap); SoftReference<Drawable> drawableSoftReference = new SoftReference<Drawable>(drawable); if(drawableSoftReference != null) { view.setBackground(drawableSoftReference.get()); }</code></pre> <p>这样的好处是</p> <p>通过软引用的get()方法,取得drawable对象实例的强引用,发现对象被未回收。在GC在内存充足的情况下,不会回收软引用对象。此时view的背景显示.</p> <p>实际情况中,我们会获取很多图片.然后可能给很多个view展示, 这种情况下很容易内存吃紧导致oom,</p> <p>内存吃紧,系统开始会GC。这次GC后,drawables.get()不再返回Drawable对象,而是返回null,这时屏幕上背景图不显示,说明在系统内存紧张的情况下,软引用被回收。</p> <p>使用软引用以后,在OutOfMemory异常发生之前,这些缓存的图片资源的内存空间可以被释放掉的,从而避免内存达到上限,避免Crash发生。</p> <p>需要注意的是,在垃圾回收器对这个Java对象回收前,SoftReference类所提供的get方法会返回Java对象的强引用,一旦垃圾线程回收该Java对象之后,get方法将返回null。所以在获取软引用对象的代码中,一定要判断是否为null,以免出现NullPointerException异常导致应用崩溃。</p> <ul> <li> <h3>弱引用 (WeakReference)</h3> <p>用法</p> <pre> <code class="language-java">WeakReference<User> sr = new WeakReference<User>(new User());</code></pre> <p>如果一个对象只具有弱引用,那么在垃圾回收器线程扫描的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。</p> <p>弱引用与软引用的根本区别在于:只具有弱引用的对象拥有更短暂的生命周期,可能随时被回收。而只具有软引用的对象只有当内存不够的时候才被回收,在内存足够的时候,通常不被回收。</p> </li> </ul> <p>使用场景,handler的使用防止内存泄露</p> <pre> <code class="language-java">import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.support.v7.app.AppCompatActivity; import java.lang.ref.WeakReference; public class MainActivity extends AppCompatActivity { private Handler handler ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); handler = new MyHandler( this ) ; new Thread(new Runnable() { @Override public void run() { handler.sendEmptyMessage( 0 ) ; } }).start() ; } private static class MyHandler extends Handler { WeakReference<MainActivity> weakReference ; public MyHandler(MainActivity activity ){ weakReference = new WeakReference<MainActivity>( activity) ; } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if ( weakReference.get() != null ){ // update android ui } } } }</code></pre> <p>在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。</p> <p>软引用,弱引用都非常适合来保存那些可有可无的缓存数据。如果这样做,当系统内存不足时,这些缓存数据会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以存在相当长的时间。</p> <ul> <li> <h3>到底什么时候使用软引用,什么时候使用弱引用呢?</h3> <p>个人认为,如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。</p> <p>还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。</p> <p>另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。</p> </li> </ul> <h3> </h3> <p> </p> <p>来自:http://www.jianshu.com/p/8488079a939b</p> <p> </p>