我们应该了解的那些事-Reference

引用的概念

Java中为我们引入了引用这个类,它可以帮助我们引用对象并且可以更高效的利用内存。

Reference<T>1 我们今天的主角,先看一下官方给它说明:

Abstract base class for reference objects. This class defines the operations common to all reference objects. Because reference objects are implemented in close cooperation with the garbage collector, this class may not be subclassed directly.

引用的结构

它三个直接子类分别是:PhantomReference<T>,SoftReference<T>, WeakReference<T>,同时它们与GC回收有着密切的关联。接下来我们了解一下Java中的垃圾回收机制和如果标记一个对象是可回收的,然后系统性的介绍一下这几个引用类型,最后我们结合着代码介绍一下这几个类是如何使用的。

关于垃圾回收

对象可达性

对象的可达性,什么类型的可达性关乎着这个对象什么时候会被回收。

  • 强可达对象(strongly reachable):
    如果一个对象通过强引用可达或者通过强引用链可达的话这种对象就成为强可及对象,这种情况下的对象垃圾回收器不予理睬。如果我们开发过程不需要垃圾回器回收该对象,就直接将该对象赋为前引用。

  • 软可达对象(weakly reachable):
    可以通过软引用访问的对象就成为软可及对象,当内存不够的时候GC会回收这类内存。

  • 弱可达对象(weakly reachable):
    如果一个对象不是强可及对象,也不是软可及对象,仅通过弱应用访问的我们称之为弱可及对象。

  • 虚可达对象(phantom reachable):
    简单来说以上都不是,仅通过虚引用访问的对象就是虚可及对象了。

  • 不可达到状态(unreachable):
    对象没有被任何类型的引用引用着,因此被回收了。我们可以称之为不可达状态

垃圾回收机制

我们知道Java中的垃圾回收都是有GC回收器自动处理的,它是一个系统级的垃圾回收(GC,Garbage Collection)线程,在GC回收的时候会触发STW(stop the world),就是说在GC回收的时候会挂起除GC线程之外的其他线程,当然native代码还是能够继续进行,频繁的GC回收会导致页面卡顿。

现在的GC回收算法大多数使用了根集(root set)这个概念,垃圾回收首先需要确定从根开始哪些是可达的和哪些是不可达的,从根集可达的对象都是活动对象,它们不能作为垃圾被回收,这也包括从根集间接可达的对象。而根集通过任意路径不可达的对象符合垃圾收集的条件,应该被回收。下面介绍几个常用的算法。

  • 引用计数法(Reference Counting Collector)
    引用计数法是唯一没有使用根集的垃圾回收的算法,该算法使用引用计数器来区分存活对象和不再使用的对象。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域生,计数器减1。

  • tracing算法(Tracing Collector)
    为了解决引用计数法所引出的问题,提出了tracing算法,它使用了根集的概念。垃圾收集器从根集开始扫描,识别出哪些对象可达,哪些对象不可达,并用某种方式标记可达对象。从而根据标记清理垃圾对象。

这里写图片描述

还有其他经过优化或解决碎片问题的算法这里就不赘述了,简单罗列一下大家有兴趣可以自行了解一下,包括compacting算法,copying算法,generation算法.

引用类型和引用队列

  • 强引用(StrongReference):强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。这里给出的是他们一句话的介绍,详细的一定要到官网上看一下哦!一定一定!

  • 软引用(SoftReference)2:官方对它的描述

    Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand.

    翻译过来可以理解为 软引用只有在内存不足时会被GC回收器回收掉。
    这么一看软引用非常适合来做cache,但事实上Android并不推荐大家使用它来做cache,因为软引用不能够提供足够的信息来告诉GC回收器,它所引用的对象是应该被回收或者应该被保持着。看到这里大家会有疑问,我们该怎么办呢?有没有能够替代软引用来做cache的呢?这里买个关子 最后的时候我们再来介绍~

  • 弱引用(WeakReference)3:官方对它的描述

    Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed. Weak references are most often used to implement canonicalizing mappings.

    这一大段的定义和解释我们可以简单的理解为 弱引用没办法阻止GC回收器回收或终结一个对象,当GC回收器扫描到弱引用时就会将其回收,同时官方告诉我们弱引用通常用来实现规范化映射.
    在知名第三方图片加载库Universal-Image-Loader中就是用了弱引用来维护Bitmap对象,从而减少bitmap对象对于内存的消耗。

  • 虚引用(PhantomReference)4:官方对它的描述

    Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed.

    虚引用所引用的对象我们称之为幽灵对象,虚引用唯一的用处就是对于引用对象的跟踪和收集,它必须与ReferenceQueue一起使用充当一个通知的角色,监控引用的对象何时放入到引用队列中。
    它可以知道对象何时会从内存被删除,这个特性可以让我们将它用于一些特殊需求。因为它所引用的对象在被放入引用队列之后不会立刻被回收,直到这个对象是一个不可达到状态或者是没有引用的时候才会被回收。

  • 引用队列(ReferenceQueue)5:官方对它的描述

    Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected.

    引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用添加到该队列中。它可以与其他引用类型配合使用,当引用对象指向的内存空间被回收后,这个引用对象就会被放入引用队列之中。

我们如何合理的使用它们?

  • SoftReference 的替代者 LruCache

    从上面的介绍来看软引用适合用来实现内存敏感的高速缓存,但是由于它所存在的问题Android推出了一个替代它的方案LruCache,看一下官方给出的例子

    int cacheSize = 4 * 1024 * 1024; // 4MiB 
    LruCache<String, Bitmap> bitmapCache = new LruCache<String,     Bitmap>(cacheSize) { 
        protected int sizeOf(String key, Bitmap value) {
            return value.getByteCount(); 
        } 
    }

LruCache引用着有限个数的或者有限大小的强引用对象,通过队列操作控制对象或者数据的更新和回收,LruCache维护着一个LinkedHashMap,由它来保存对象的<K, V>
大家看到HashMap就会想,它是一个线程不安全的的容器类,那在并发操作的时候怎么办?这点不用担心LruCache已经做了相应的处理,在关键的方法上都加了同步锁。
现在大多数的数据缓存或者自己封装的图片加载类都是用了LruCache,可以很好的帮助我们解决OOM的问题。

  • WeakReference

    • 典型例子 Universal-Image-Loader 6
      ImagLoader在对于内存缓存时,没有使用我们上面所说的LruCache,而是自定义了一个
      抽象类BaseMemoryCache和接口MemoryCache,使用SynchronizedMap<String, Reference<Bitmap>>来保存缓存的Bitmap对象,这里它使用Reference<T>为了给子类提供扩展空间,通过抽象方法createReference(Bitmap value)实现扩展。
      ImageLoader中提供非常多的缓存回收算法供开发者使用,例如最大优先删除,最少使用优先删除,最久优先删除等等,但是ImageLoader在实现的时候是有瑕疵的。所有LimitedMemoryCache子类都有个问题,就是 Bitmap 虽然通过WeakReference包装,但实际根本不会被虚拟机回收,因为他们子类中同时都保留了 Bitmap 的强引用。只有在这个强引用被删除了WeakReference才有可能被回收,大都是 UIL 早期实现的版本,不推荐使用。所以大家使用的时候如果需要上述的算法支持,继承BaseMemoryCache重写。或者使用两个没有问题的缓存LruMemoryCacheWeakMemoryCache

      回归正题,虽然有些许的不足但是其设计理念还是很不错的,ImageLoader中所实现的这些回收机制,基本上都实现了createReference(Bitmap value)方法(LruMemoryCache除外)。统一都是使用了WeakReference,源码如下

      @Override
      protected Reference<Bitmap> createReference(Bitmap value) {
          return new WeakReference<Bitmap>(value);
      }
      

      通过WeakReference的包装,利用其回收机制从而减少Bitmap对象对内存的压力。

    • 防止内存泄露
      我们都知道最容易出现内存泄漏的方式就是:Activity的内部类引用Activity实例,这样会导致Activity不能被释放。Handler是我们最常用的线程间消息通讯方式,它也是最容易发生内存泄漏的罪魁祸首之一。那我们我就可以通过使用WeakReference来包装Activity、Context、Fragment,从而防止内存泄漏的发生。

      static class MyHandler extends Handler{
          WeakReference<XXXActivity> mactivity;
      
          public MyHandler(XXXActivity activity){
              mactivity = new WeakReference<XXXActivity>(activity);
          }
      
          @Override
          public void handleMessage(Message msg) {
              super.handleMessage(msg);           
                  switch (msg.what) {
                  }
          }
      }
      

      Asynctask是我们另一个容易发生内存泄漏的,所以我们也可以通过WeakReference来包装。系统中提供了一个我们无法继承的模板WeakAsyncTask,我们可以模仿其实现一个WeakAsyncTask

      public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget> extends  
              AsyncTask<Params, Progress, Result> {  
          protected WeakReference<WeakTarget> mTarget;  
      
          public WeakAsyncTask(WeakTarget target) {  
              mTarget = new WeakReference<WeakTarget>(target);  
          }  
      
          @Override  
          protected final void onPreExecute() {  
              final WeakTarget target = mTarget.get();  
              if (target != null) {  
                  this.onPreExecute(target);  
              }  
          }  
      
          @Override  
          protected final Result doInBackground(Params... params) {  
              final WeakTarget target = mTarget.get();  
              if (target != null) {  
                  return this.doInBackground(target, params);  
              } else {  
                  return null;  
              }  
          }  
      
          @Override  
          protected final void onPostExecute(Result result) {  
              final WeakTarget target = mTarget.get();  
              if (target != null) {  
                  this.onPostExecute(target, result);  
              }  
          }  
      
          protected void onPreExecute(WeakTarget target) {  
              // No default action  
          }  
      
          protected abstract Result doInBackground(WeakTarget target, Params... params);  
      
          protected void onPostExecute(WeakTarget target, Result result) {  
              // No default action  
          }  
      } 

结语

内存优化门道很多,引用只是其中一部分。下篇会介绍一下关于内存优化的部分。
这次关于引用的介绍就到这里了~ 欢迎大家多提意见多交流~

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: ug1144-petalinux-tools-reference-guide中文版是一份Petaliunx工具参考指南的中文版,主要介绍了Petaliunx工具的使用方法和步骤,包括安装Petaliunx工具、创建项目、配置内核、编译设备树、配置rootfs等方面内容。对于Petaliunx开发者来说,这份指南是非常重要的参考资料。它详细介绍了Petaliunx工具的各个组成部分,让使用者能够深入了解每个工具的作用和用法,并且提供了清晰的步骤指引,让使用者能够更加便捷地使用Petaliunx工具进行开发。 此外,这份指南还介绍了Petaliunx的一些重要特性和配置项,如交叉编译、设备树、驱动模块、网络配置等,通过对这些特性的深入解析,使用者能够更加全面地了解Petaliunx的开发流程和技术细节。与英文版相比,中文版使用了通俗易懂的语言和丰富的实例,更容易被国内Petaliunx开发者接受和使用。 总之,ug1144-petalinux-tools-reference-guide中文版是一份非常重要的Petaliunx工具参考指南,对于Petaliunx开发者来说,是一个不可或缺的参考资料。 ### 回答2: ug1144-petalinux-tools-reference-guide是一本详细介绍PetaLinux工具的参考指南,此版本为中文版。 PetaLinux工具是一种用于嵌入式Linux设备开发的平台,它支持Zynq和MicroBlaze架构的Xilinx FPGA器件。该工具提供了一整套工具链,包括交叉编译器、SDK、bootloader、内核以及文件系统等,使得软件开发人员能够快速构建Linux嵌入式系统。 该参考指南主要包含了PetaLinux工具的基本使用方法、配置选项和命令参数,通过该指南,用户可以深入学习PetaLinux工具的使用方法,并能够熟练地配置、编译和部署嵌入式系统。另外,该指南还提供了详细的实例和示意图,使得用户能够更加清晰地理解和学习PetaLinux工具的使用。 总之,ug1144-petalinux-tools-reference-guide中文版是一本非常有价值的参考指南,它将对嵌入式Linux开发人员的工作和学习提供广泛的帮助。 ### 回答3: ug1144-petalinux-tools-reference-guide是一份有关PetaLinux工具的参考手册,该手册介绍了使用PetaLinux工具开发嵌入式Linux系统的过程。该手册中文版介绍了PetaLinux固件构建工具(PetaLinux tools)以及这些工具的配置和使用方法。 PetaLinux是一种嵌入式Linux系统工具,可用于生成基于Xilinx FPGA的SoC系统,以及在Zynq All Programmable SoCs等硬件中运行的Linux系统。使用PetaLinux工具可以快速构建Linux系统,以满足更广泛的软件需求。 该手册包含了PetaLinux工具中常用功能的详细介绍,包括Linux内核的配置、设备树的生成、文件系统的构建和bootloader的生成等等。对于需要将PetaLinux嵌入式系统应用于硬件开发的人员来说,该手册为他们提供了非常有用的参考。 总体而言,ug1144-petalinux-tools-reference-guide中文版是一份很好的参考手册,其详细且易于理解的介绍和示例可帮助嵌入式开发者更快速地构建出可靠的嵌入式Linux系统。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值