来源: Android性能优化之巧用软引用与弱引用优化内存使用 – 总李写代码 – 博客园
前言:
从事Android开发的同学都知道移动设备的内存使用是非常敏感的话题,今天我们来看下如何使用软引用与弱引用来优化内存使用。下面来理解几个概念。
1.StrongReference(强引用)
强引用是我们最最常见的一种,一般我们在代码中直接通过new出来的对象等,都是强引用,强引用只要存在没有被销毁,内存就不会被系统回收。我们以生成Bitmap为例如下:
Bitmap imageBitmap = readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5);
生成Bitmap代码:
public Bitmap readBitmapFromResource(Resources resources, int resourcesId) { BitmapFactory.Options options = new BitmapFactory.Options(); return BitmapFactory.decodeResource(resources, resourcesId, options); }
2.SoftReference(软引用)
软引用是用来描述一些有用但并不是必需的对象,在内存严重不足的情况下会被系统回收,如果该对象可能会经常使用的,就尽量用软引用。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。这里还是以缓存Bitmap为例:
SoftReference<Bitmap> softReference = new SoftReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5)); Bitmap bitmap = softReference.get();
3.WeakReference(弱引用)
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象,WeakReference 的强度又明显低于 SoftReference,所以如果该对象不被使用的可能性更大些,就可以用弱引用。还是以缓存Bitmap为例:
WeakReference<Bitmap> weakReference = new WeakReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5)); Bitmap bitmap1 = weakReference.get();
4.PhantomReference(虚引用)
虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。还是以缓存Bitmap为例:
ReferenceQueue<Bitmap> queue = new ReferenceQueue<Bitmap>(); PhantomReference<Bitmap> phantomReference = new PhantomReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5),queue); Bitmap bitmap2 = phantomReference.get();
5.几种引用被回收概念测试
从上面的分析可以看出内存被系统回收的概率从小到大是:虚引用–弱引用–软引用–强引用,我们写个程序来验证一下。
public class MainActivity extends AppCompatActivity { private LinearLayout request_layout; private PhantomReference<Bitmap> phantomReference; private WeakReference<Bitmap> weakReference; private SoftReference<Bitmap> softReference; private Bitmap strongReference; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); request_layout = (LinearLayout) findViewById(R.id.request_layout); findViewById(R.id.request_btn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { testReference(); } }); } private void testReference() { //模拟内存使用 往一个布局中不断加入ImageView来模拟内存使用 ImageView imageView = new ImageView(this); Bitmap imageBitmap = readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5); imageView.setImageBitmap(imageBitmap); request_layout.addView(imageView); if (strongReference == null) { strongReference = readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5); } Log.e("Reference", "StrongReference---->" + strongReference); if (softReference == null) { softReference = new SoftReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5)); } Bitmap bitmap = softReference.get(); Log.e("Reference", "SoftReference---->" + bitmap); if (weakReference == null) { weakReference = new WeakReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5)); } Bitmap bitmap1 = weakReference.get(); Log.e("Reference", "WeakReference---->" + bitmap1); if (phantomReference == null) { ReferenceQueue<Bitmap> queue = new ReferenceQueue<Bitmap>(); phantomReference = new PhantomReference<Bitmap>(readBitmapFromResource(getResources(), R.mipmap.bg_post_activity_5), queue); } Bitmap bitmap2 = phantomReference.get(); Log.e("Reference", "PhantomReference---->" + bitmap2); } public Bitmap readBitmapFromResource(Resources resources, int resourcesId) { BitmapFactory.Options options = new BitmapFactory.Options(); return BitmapFactory.decodeResource(resources, resourcesId, options); } }
第一次点击打印信息:
通过打印信息可以虚引用直接回收掉了,或者可以说直接不存在引用。
接下来多次点击打印信息:
在模拟内存使用越来越紧张的情况下,并没有出现先回收弱引用,再回收软引用,而是两个一并回收掉了,其实按照Java正常引用顺序是软引用强于弱引用,但是从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用像弱引用一样变得不再可靠。所以图片缓存不再使用软引用而采用LRU算法。但是强引用一直毅力不倒。
总结:
从上面的介绍及测试对比可以得知,如果我们比较在意APP的性能的话,我们可以把哪些不常用并且占用内存比较大的对象用软引用或者弱引用来做缓存处理,鉴于保险起见,可以酌情选择使用弱引用还是软引用,实测下来二者被回收的概率相差无几。